解决OBS输入源拉取H.265视频流无图像的问题
起因
在测试户外直播时发现, 本地 5G 信号时有时无, 4G 信号上行带宽很小, 直播推流到服务器时带宽压力很大, 于是干脆把推流编码更改为 H.265, 在电脑上使用 OBS 拉流时, 只有声音, 没有图像, 试着使用 ffplay 拉流, 报错: “Video code (c) not implemented”.
关于出错的具体原因在上一篇文章RTMP 协议与 H.265 编码中已经解释过, 即 RTMP 支持 H.265 有两套方案, 由于技术惯性, 目前国内拉流得到的 H.265 视频流编码大概率还是使用自定义 CodecID(CodecID=12 ) 这种方法解决的, 虽然OBS已经通过 Enhanced-RTMP 实现了 RTMP 的 H.265 支持, 但显然 自定义 CodecID 的这种解决方案 OBS 是不支持的, 但幸运的是 OBS 是开源软件, 我们可以自己动手修改.
编译 obs-deps
obs-deps 是 OBS 依赖的一些项目, 比如 Qt, ffmpeg 等, 由于 ffmpeg 不支持自定义 CodecID 这种使 RTMP 支持 H.265 的方案, 自然 OBS 也就不支持了, 所以需要修改 ffmpeg 的源代码, 然后编译 obs-deps, 再编译 OBS 就可以了.
因为我主要使用 Windows 平台, 故以下操作均在 Windows10 下进行, 网络最好挂代理, 否则无法访问 Github.
- 安装 VisualStudio
从微软官网下载VisualStudio2022社区版即可, 需要安装 Clang 支持, 在 “单个组件” 页面中搜索 clang, 勾选 “对LLVM (clang-cl) 工具集的 MSBuild 支持” 和 “适用于 Windows 的 C++ Clang 编译器” 两个选项.
- 安装CMake
从 CMake官网 下载CMake并安装.
- 安装 Powershell 7
obs-deps 是在 windows 使用 powershell 脚本编译, 需要 powershell core 7.2+, 从 Github下载 PowerSehll7 以上的版本安装即可.
- 安装 NASM 和 YASM
这两个是汇编器, 编译 ffmpeg 源代码时要用到. GitHub 有个叫 ShiftMediaProject 的项目, 可使 ffmpeg 在 Windows 系统上使用 VS 编译, 他们提供了用于 VisualStudio 集成的 VSNASM 和 VSYASM, 下载安装即可.
- 安装7-zip
从 7-zip 官网下载安装即可.
- 下载 obs-deps 源代码
由于只需要修改编译 ffmpeg 相关的代码, 不需要编译所有依赖, 故要对编译脚本做一些修改.
- 修改 Build-Dependencies.ps1 第 6 行, 将
更改为$PackageName = 'dependencies'
$PackageName = 'ffmpeg'
, 使得脚本只编译 ffmpeg:
0[CmdletBinding()]
1param(
2 [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
3 [string] $Configuration = 'Release',
4 [ValidateSet('dependencies', 'ffmpeg', 'qt')]
5 [string] $PackageName = 'ffmpeg',
6 [string[]] $Dependencies,
7 [ValidateSet('x86', 'x64')]
8 [string] $Target,
9 [switch] $Clean,
10 [switch] $Quiet,
11 [switch] $Shared,
12 [switch] $SkipAll,
13 [switch] $SkipBuild,
14 [switch] $SkipDeps,
15 [switch] $SkipUnpack,
16 [switch] $VSPrerelease
17)
- 修改 utils.pwsh/invoke-SafeWebRequest.ps1 第 60 行, 给 curl 加上代理, 否则下载某些包时速度相当感人.
51 if ( $Headers.count -gt 0 ) {
52 $HeaderStrings = @()
53
54 $Headers.GetEnumerator() | ForEach-Object {
55 $Header = $_
56 $HeaderStrings += "-H `"$($Header.key): $($Header.Value)`""
57 }
58 }
59
60 Invoke-External curl -x socks5://127.0.0.1:7890 --fail --location $(if ( $Env:CI -eq $null ) { '--progress-bar' }) --output $OutFile @HeaderStrings $Uri
61
62 $NewHash = Get-FileHash -Path $OutFile -Algorithm $Algorithm
63 }
接下来运行 PowerShell7, cd 到 obs-deps 目录中, 先执行命令:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
然后执行
.\Build-Dependencies.ps1
脚本将开始自动下载和编译各模块
这一次编译主要是让脚本自动下载所依赖的其它模块, 等编译完成后就可以更改代码了.
首先从 Github 下载 ffmpeg-rtmp-h265, 下载时注意分支, OBS 30.0 使用的是 ffmpeg 6.0, 所以我这里下载的是 6.0 分支的.
编译时下载的文件均在 windows_build_temp 目录中, 因为脚本在编译时是 checkout 出特定分支再编译的, 所以直接在该目录中修改文件不可行, 最好的办法还是打补丁.
涉及的文件有 3 个, 分别是位于 windows_build_temp/FFmpeg/libavformat/
目录中的 flv.c
, flvdec.c
, flvenc.c
, 使用如下命令制作补丁文件:
1diff -u windows_build_temp/FFmpeg/libavformat/flv.h ffmpeg_rtmp_h265/flv.h >> 0003-flv_h.patch
2diff -u windows_build_temp/FFmpeg/libavformat/flvdec.c ffmpeg_rtmp_h265/flvdec.c >> 0003-flvdec.patch
3diff -u windows_build_temp/FFmpeg/libavformat/flvenc.c ffmpeg_rtmp_h265/flvenc.c >> 0003-flvenc.patch
修改这 3 个文件的头部路径, 类似这样:
1--- ./libavformat/flvenc.c 2024-01-25 18:13:16.075354000 +0800
2+++ ./libavformat/flvenc.c 2024-01-25 11:27:07.029420400 +0800
3@@ -1,1131 +1,1204 @@
即只保留 libavformat
这一层路径和文件名, 然后将这 3 个文件移动到 deps.ffmpeg/patches/FFmpeg/
, 然后修改 deps.ffmpeg/99-ffmpeg.ps1
, 在 [array] $Patches
数组中新增上面三个补丁的数据, 其中 HashSum
是补丁文件的 sha256
, 最后结果类似这样:
1param(
2 [string] $Name = 'FFmpeg',
3 [string] $Version = '6.1.1',
4 [string] $Uri = 'https://github.com/FFmpeg/FFmpeg.git',
5 [string] $Hash = "e38092ef9395d7049f871ef4d5411eb410e283e0",
6 [array] $Patches = @(
7 @{
8 PatchFile = "${PSScriptRoot}/patches/FFmpeg/0003-flv_h.patch"
9 HashSum = "f439e5aa85e0931fbe902f7b8060c4044f8b64e27ae4891fc8ec490e3f88fbe0"
10 }
11 @{
12 PatchFile = "${PSScriptRoot}/patches/FFmpeg/0003-flvenc.patch"
13 HashSum = "8e2d0e18cc7cdf621abe26f29292fa1a233d8d28389cce04d7dc65e18785f944"
14 }
15 @{
16 PatchFile = "${PSScriptRoot}/patches/FFmpeg/0003-flvdec.patch"
17 HashSum = "514ff9e754fade29fa3bf91f7d18317d2b4c717b67c37aa786bae5ddbc115665"
18 }
19 @{
20 PatchFile = "${PSScriptRoot}/patches/FFmpeg/0002-libaomenc-presets-Windows.patch"
21 HashSum = "cec898b957fc289512094fc2c4e6a61d6872f716e4a643fb970c599a453a33f4"
22 }
23 )
24)
再次使用 PowerShell 7 执行 Build-Dependencies.ps1
, 编译完成后会生成一个压缩包, OBS 依赖的库都打包到这里了.
因为 OBS 是使用动态库载入 ffmpeg 的, 按说从刚刚得到的压缩包中解压出 bin 文件夹, 覆盖掉 OBS 安装目录中的 bin/64bit/
中同名文件就可以了, 应该不会有什么问题.
编译 obs-studio
如果想编译 obs-studio的话, 这里是 OBS 官方提供的编译指南, 已经写的很详细了.
- 获取 OBS 源码
建议使用命令行 git clone --recursive https://github.com/obsproject/obs-studio.git
下载源码, 因为项目中有一些子模块, 如果下载的是 Releases 中的 Source Code 源代码包的话, 这些子模块是没有的, 需要手动下载并放到对应文件夹.
- 配置工程
克隆完源代码后就可以配置工程了, 打开 CMake GUI, 选择源码文件夹, 要构建的预设和要保存编译好的二进制文件的文件夹, 然后按 Configure 即可.
不过在配置时可能会出现如下错误:
这是因为 OBS 在配置工程时会通过CMake下载一些依赖库, 因为众所周知的原因, Github 很难访问, 就算使用了某些手段, CMake 下载时也很容易失败, 遇到这种情况, 直接从 CMake 的出错信息中复制下载地址, 使用其它下载工具下载, 完成后移动到源码文件夹中的 .deps 文件夹中再次配置即可.
另外刚刚编译 obs-deps 得到的压缩包此时就可以派上用场了, 复制一份到 .deps 文件夹中, 覆盖掉同名文件即可, 如果名称不一致, 最好下载对应的 obs-deps 编译, 以免出现问题.
- 版本号
如果直接配置工程后编译的话 OBS 标题栏显示的版本号为当前版本附带 Git 提交纪录的 hash 一部分, 长长的一串看起来不美观, 要自定义版本号的话, 在 CMake GUI 界面点击 Add Entry, 增加一个变量 OBS_VERSION_OVERRIDE, 设置其值即可.
完成以上操作后在 CMake GUI 界面点击 Generate 生成 VS 工程, 然后点击 Open Project, 会自动在 VS 中打开已经配置好的 VS 工程, 选择在解决方案资源管理器窗口中右击 ALL_BUILD
, 选择构建, 即可开始编译, 大约两三分钟就可以编译完成.
接下来右击 PACKAGE
, 也选择 构建, VS 会将所有必要的文件打包为zip, 实际上就是生成一个绿色版的安装包, 可以拷贝到其它电脑使用.