Spaces:
Running
Running
File size: 6,167 Bytes
d646dd7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | """编码器选择单测(v1.0.9)—— 假探针,不跑真实 ffmpeg。
覆盖:auto 三档顺序(nvenc → x265 达标 → x264)、x265 门槛判定、
显式指定(跳门槛/失败回退)、None 兜底、进程内缓存、参数映射。
用法:
venv/bin/python tools/tests/test_encoder_select.py
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import core.encoder_probe as ep
from config import settings
FAILS = []
def check(cond, msg):
if cond:
print(f" PASS: {msg}")
else:
print(f" FAIL: {msg}")
FAILS.append(msg)
PASS_PROBE = lambda *a, **k: {"per_stream_fps": [55.0, 55.0],
"min_fps": 55.0, "passed": True,
"threshold_fps": 45.0, "streams": 2,
"frames": 45, "elapsed_s": 1.0}
LOW_PROBE = lambda *a, **k: {"per_stream_fps": [20.0, 20.0],
"min_fps": 20.0, "passed": False,
"threshold_fps": 45.0, "streams": 2,
"frames": 45, "elapsed_s": 2.3}
def main():
# 环境:单个假 ffmpeg 二进制,支持全部编码器
ep._FFMPEG_CACHE = ["/fake/ffmpeg"]
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx264")] = True
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx265")] = True
ep._ENCODER_CACHE[("/fake/ffmpeg", "hevc_nvenc")] = True
# 1. auto:nvenc 探针可用 → 首选 nvenc
ep._SELECT_CACHE.clear()
ep.probe_nvenc = lambda *a, **k: True
c = ep.select_encoder("auto", 1280, 960, 30, 2)
check(c is not None and c.encoder == "hevc_nvenc",
f"auto 链首选 hevc_nvenc(得到 {c and c.encoder})")
check(c is not None and c.codec == "HEVC" and c.args[-1] == "hvc1",
"nvenc 选择带 HEVC/hvc1")
check(c is not None and c.args == ep.encoder_args("hevc_nvenc"),
"nvenc 参数映射一致")
# 2. nvenc 失败 → x265 达标 → libx265
ep._SELECT_CACHE.clear()
ep.probe_nvenc = lambda *a, **k: False
ep.probe_x265 = PASS_PROBE
c = ep.select_encoder("auto", 1280, 960, 30, 2)
check(c is not None and c.encoder == "libx265"
and c.probe["min_fps"] == 55.0,
"nvenc 失败且 x265 达标 → libx265")
check(c is not None and c.crf == settings.RECORD_VIDEO_CRF,
"x265 crf=RECORD_VIDEO_CRF")
# 3. x265 不达标 → x264 兜底
ep._SELECT_CACHE.clear()
ep.probe_x265 = LOW_PROBE
c = ep.select_encoder("auto", 1280, 960, 30, 2)
check(c is not None and c.encoder == "libx264"
and c.crf == settings.RECORD_VIDEO_X264_CRF,
"x265 不达标 → x264 兜底(CRF 回退档)")
# 4. 显式 x265:跳过速度探针(探针被调用即失败)
ep._SELECT_CACHE.clear()
def boom(*a, **k):
raise AssertionError("probe should not be called")
ep.probe_x265 = boom
ep.probe_nvenc = boom
try:
c = ep.select_encoder("x265", 1280, 960, 30, 2)
ok = c is not None and c.encoder == "libx265"
except AssertionError:
ok = False
check(ok, "显式 x265 跳过速度门槛(不调探针)")
# 5. 显式 nvenc 失败 → auto 链回退(此处 x265 达标)
ep._SELECT_CACHE.clear()
ep.probe_nvenc = lambda *a, **k: False
ep.probe_x265 = PASS_PROBE
c = ep.select_encoder("nvenc", 1280, 960, 30, 2)
check(c is not None and c.encoder == "libx265",
"显式 nvenc 失败 → auto 链回退")
# 6. 显式 x264:不调任何探针
ep._SELECT_CACHE.clear()
calls = {"n": 0}
ep.probe_nvenc = lambda *a, **k: calls.__setitem__("n", calls["n"] + 1) or True
ep.probe_x265 = lambda *a, **k: calls.__setitem__("n", calls["n"] + 1) or PASS_PROBE()
c = ep.select_encoder("x264", 1280, 960, 30, 2)
check(c is not None and c.encoder == "libx264" and calls["n"] == 0,
"显式 x264 跳过探针直接采用")
# 7. 支持表缺 libx264 → None(全链失败)
ep._SELECT_CACHE.clear()
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx264")] = False
ep.probe_nvenc = lambda *a, **k: False
ep.probe_x265 = LOW_PROBE
c = ep.select_encoder("auto", 1280, 960, 30, 2)
check(c is None, "无任何可用编码器 → None")
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx264")] = True
# 8. 显式 x265 但二进制不支持 → auto 链回退 + 日志播报
ep._SELECT_CACHE.clear()
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx265")] = False
ep.probe_nvenc = lambda *a, **k: False
ep.probe_x265 = PASS_PROBE
logs = []
c = ep.select_encoder("x265", 1280, 960, 30, 2, log=logs.append)
check(c is not None and c.encoder == "libx264"
and any("回退" in l for l in logs),
"显式 x265 不支持 → auto 回退且播报原因")
ep._ENCODER_CACHE[("/fake/ffmpeg", "libx265")] = True
# 9. 进程内缓存:同参第二次不重探(返回同一对象)
ep._SELECT_CACHE.clear()
calls = {"n": 0}
ep.probe_nvenc = lambda *a, **k: calls.__setitem__("n", calls["n"] + 1) or True
c1 = ep.select_encoder("auto", 640, 480, 30, 1)
n1 = calls["n"]
c2 = ep.select_encoder("auto", 640, 480, 30, 1)
check(c1 is c2 and calls["n"] == n1, "进程内缓存:同参不重探")
# 10. 无 ffmpeg 二进制 → None
ep._FFMPEG_CACHE = []
ep._SELECT_CACHE.clear()
check(ep.select_encoder("auto", 640, 480, 30, 1) is None,
"无 ffmpeg → None")
# 11. 参数映射表(x264 用回退档、x265/nvenc 用 HEVC 档)
check(ep.encoder_args("libx265")[-1] == "hvc1", "libx265 带 hvc1")
check(ep.encoder_args("libx265", 28)[1] == "28", "libx265 crf 透传")
check("-cq" in ep.encoder_args("hevc_nvenc"), "nvenc 用 -cq")
check(ep.encoder_args("libx264")[-2:] ==
["-crf", str(settings.RECORD_VIDEO_X264_CRF)],
"libx264 用回退 CRF")
print()
if FAILS:
print(f"FAIL: {len(FAILS)} 项未通过")
return 1
print("PASS: encoder_select 单测全部通过")
return 0
if __name__ == "__main__":
sys.exit(main())
|