This commit is contained in:
root
2026-05-18 05:39:26 +00:00
parent ab45c6c329
commit f1145e3a36
2 changed files with 21 additions and 21 deletions

View File

@@ -126,11 +126,11 @@ def _resolve_youtube_push_config_file(config_file: str | None = None, *, base_di
def _load_youtube_push_settings(config_file: str) -> dict[str, str | bool]: def _load_youtube_push_settings(config_file: str) -> dict[str, str | bool]:
settings: dict[str, str | bool] = { settings: dict[str, str | bool] = {
"use_rtmps": True, "use_rtmps": True,
"b_v": "2800k", "b_v": "4000k",
"maxrate": "3200k", "maxrate": "4500k",
"bufsize": "5600k", "bufsize": "8000k",
"stream_key": "", "stream_key": "",
"fast_audio": True, "fast_audio": False,
} }
if not os.path.exists(config_file): if not os.path.exists(config_file):
return settings return settings
@@ -144,8 +144,8 @@ def _load_youtube_push_settings(config_file: str) -> dict[str, str | bool]:
v = (v or "").strip() v = (v or "").strip()
if k == "rtmps" and v.lower() in ("0", "false", ""): if k == "rtmps" and v.lower() in ("0", "false", ""):
settings["use_rtmps"] = False settings["use_rtmps"] = False
elif k == "fast_audio" and v.lower() in ("1", "true", "yes", "", "", "on"): elif k == "fast_audio":
settings["fast_audio"] = True settings["fast_audio"] = v.lower() in ("1", "true", "yes", "", "", "on")
elif k == "bitrate" and v: elif k == "bitrate" and v:
try: try:
n = int(v) n = int(v)
@@ -182,10 +182,10 @@ def start_douyin_youtube_ffplay(
rtmps_base = "rtmps://a.rtmp.youtube.com/live2/" rtmps_base = "rtmps://a.rtmp.youtube.com/live2/"
rtmp_base = "rtmp://a.rtmp.youtube.com/live2/" rtmp_base = "rtmp://a.rtmp.youtube.com/live2/"
use_rtmps = True use_rtmps = True
# 720p 竖屏 YouTube 建议 46 Mbps,偏低易触发「数据不足/卡顿」提示 # 720p 竖屏,符合 YouTube 推荐:46 Mbps、2s 关键帧、AAC 128k
b_v, maxrate, bufsize = "2800k", "3200k", "5600k" b_v, maxrate, bufsize = "4000k", "4500k", "8000k"
stream_key = None stream_key = None
fast_audio = True # 默认轻量音频链,必要时可在配置里关闭 fast_audio = False # 默认恢复强化音频链,必要时可在配置里开启轻量模式
if os.path.exists(config_file): if os.path.exists(config_file):
config = configparser.ConfigParser() config = configparser.ConfigParser()
@@ -195,8 +195,8 @@ def start_douyin_youtube_ffplay(
v = (v or "").strip() v = (v or "").strip()
if k == "rtmps" and v.lower() in ("0", "false", ""): if k == "rtmps" and v.lower() in ("0", "false", ""):
use_rtmps = False use_rtmps = False
elif k == "fast_audio" and v.lower() in ("1", "true", "yes", "", "", "on"): elif k == "fast_audio":
fast_audio = True fast_audio = v.lower() in ("1", "true", "yes", "", "", "on")
elif k == "bitrate" and v: elif k == "bitrate" and v:
try: try:
n = int(v) n = int(v)
@@ -342,14 +342,13 @@ def start_douyin_youtube_ffplay(
except Exception as e: except Exception as e:
print(f"[WARN] NVENC 检测失败,回退软件编码: {e}") print(f"[WARN] NVENC 检测失败,回退软件编码: {e}")
_cpu_n = os.cpu_count() or 4 _filter_threads = "4"
_filter_threads = str(min(2, max(1, _cpu_n // 2)))
# ====================== 开源音频处理:背景音乐弱化 + 防版权 ====================== # ====================== 开源音频处理:背景音乐弱化 + 防版权 ======================
_script_dir = os.path.dirname(os.path.abspath(__file__)) _script_dir = os.path.dirname(os.path.abspath(__file__))
_ffmpeg_cwd = None _ffmpeg_cwd = None
if fast_audio: if fast_audio:
print("[INFO] fast_audio=开:轻量音频链(无 arnndn/长 EQ减轻 CPU 以利 speed≥1.0") print("[INFO] fast_audio=开:使用轻量音频链")
_af_chain = "afftdn=nf=-26:tn=1,aresample=async=1:first_pts=0" _af_chain = "afftdn=nf=-26:tn=1,aresample=async=1:first_pts=0"
else: else:
# 优先 RNNoise(arnndn),无模型则用 afftdnaphaser 相位扰动可干扰 Content ID 指纹 # 优先 RNNoise(arnndn),无模型则用 afftdnaphaser 相位扰动可干扰 Content ID 指纹
@@ -372,7 +371,7 @@ def start_douyin_youtube_ffplay(
print("[INFO] 未找到 arnndn 模型,使用 afftdn 降噪(可克隆 richardpl/arnndn-models 到 arnndn-models/") print("[INFO] 未找到 arnndn 模型,使用 afftdn 降噪(可克隆 richardpl/arnndn-models 到 arnndn-models/")
_af_denoise = "afftdn=nf=-26:tn=1," _af_denoise = "afftdn=nf=-26:tn=1,"
# 仅在明确需要时才启用重音频链,默认优先保 CPU # 强化版:收紧带通 + 频率偏移 + 音高微调 + 多段 EQ + 相位扰动
_af_chain = ( _af_chain = (
_af_denoise _af_denoise
+ "highpass=f=200,lowpass=f=3800," + "highpass=f=200,lowpass=f=3800,"
@@ -389,14 +388,13 @@ def start_douyin_youtube_ffplay(
+ "aresample=async=1:first_pts=0" + "aresample=async=1:first_pts=0"
) )
# 720p 竖屏低耗档:更低帧率和码率,优先保证 2 路并发可用 _video_mid = ["-vsync", "cfr", "-g", "60", "-keyint_min", "60", "-r", "30",
_video_mid = ["-vsync", "cfr", "-g", "30", "-keyint_min", "30", "-r", "15",
"-bf", "0", "-sc_threshold", "0"] "-bf", "0", "-sc_threshold", "0"]
if codec_v == "libx264": if codec_v == "libx264":
_video_mid += ["-x264-params", "force-cfr=1:scenecut=0:open_gop=0:sync-lookahead=0"] _video_mid += ["-x264-params", "force-cfr=1:scenecut=0:open_gop=0:sync-lookahead=0"]
# -re按源时间戳节奏拉流避免突发过快不用 realtime/arealtime输入抖动时易让 YouTube 判为数据不足) # -re按源时间戳节奏拉流避免突发过快不用 realtime/arealtime输入抖动时易让 YouTube 判为数据不足)
_vf_pad = ( _vf_pad = (
"fps=15,scale=720:1280:force_original_aspect_ratio=decrease:flags=bilinear," "fps=30,scale=720:1280:force_original_aspect_ratio=decrease:flags=bilinear,"
"pad=720:1280:(ow-iw)/2:(oh-ih)/2:black" "pad=720:1280:(ow-iw)/2:(oh-ih)/2:black"
) )
_ffmpeg_pre_input = [] _ffmpeg_pre_input = []

View File

@@ -18,7 +18,7 @@ class YoutubeRuntimeTests(unittest.TestCase):
self.assertEqual(normalize_youtube_video_save_type("ts", "tiktok"), "TS") self.assertEqual(normalize_youtube_video_save_type("ts", "tiktok"), "TS")
self.assertEqual(normalize_youtube_video_save_type("unknown", "tiktok"), "TS") self.assertEqual(normalize_youtube_video_save_type("unknown", "tiktok"), "TS")
def test_push_defaults_use_lighter_profile(self) -> None: def test_push_defaults_restore_full_profile(self) -> None:
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from douyin_youtube_ffplay import _load_youtube_push_settings from douyin_youtube_ffplay import _load_youtube_push_settings
@@ -27,8 +27,10 @@ class YoutubeRuntimeTests(unittest.TestCase):
cfg = Path(tmp) / "youtube.ini" cfg = Path(tmp) / "youtube.ini"
cfg.write_text("[youtube]\nkey = abc\n", encoding="utf-8") cfg.write_text("[youtube]\nkey = abc\n", encoding="utf-8")
settings = _load_youtube_push_settings(str(cfg)) settings = _load_youtube_push_settings(str(cfg))
self.assertEqual(settings["b_v"], "2800k") self.assertEqual(settings["b_v"], "4000k")
self.assertTrue(settings["fast_audio"]) self.assertEqual(settings["maxrate"], "4500k")
self.assertEqual(settings["bufsize"], "8000k")
self.assertFalse(settings["fast_audio"])
def test_arm_falls_back_from_vaapi_to_v4l2m2m(self) -> None: def test_arm_falls_back_from_vaapi_to_v4l2m2m(self) -> None:
enc, note = resolve_live_video_encoder( enc, note = resolve_live_video_encoder(