Files
MeowOS-For-R3P2/patch_v2/ncm_app/bench/degrade_sim.c
T
MeowOS bdd9e898f4 feat(ncm §20): 音质体系全量——八档UI/VIP检测缓存/逐级降级下首恢复/fee门禁/默认极高HQ
- core: 9档枚举(顺序=降级链, DOLBY映射保留不进UI/链) + level_key(sky/sky_c51)
  + level_gate(CVIP/SVIP门槛) + song_url surround档附加immerseType=c51
  + song_url_auto全档表降级(out_level回填/响应level静默降档采信/try_fn可注入)
  + vip_info(weapi v2优先v1兜底) + parse_vip_info离线可测 + NCM_ERR_VIP(-15)
- app/logic/account.c(新): account.json会员态缓存(uid/nick/vip/双expire/checked_at)
  刷新=登录后/超1天/到期后各一次不轮询; account_vip本地到期降档视图
- player_manager: 用户档按会员钳顶后逐级降级(不硬拒,缓存键仍用户档),
  fee∈{1,4}且非会员→NCM_ERR_VIP零请求(缓存命中优先), eff_level回填
- UI: 设置页八档(规格+门槛+未解锁标注,允许选定), 播放页时间行中央
  生效档/已降级至xx小字, VIP专享文案; 默认档Hi-Res→极高HQ(存量设置保留)
- 验证(0真实请求): degrade_sim 90例矩阵全PASS; player_flow_sim P1-P8全PASS;
  test_ncm_core vip解析9例宿主+qemu全PASS; 窗口/分页/缓存/封面/audiod sim全PASS;
  mipsel -Wall -Wextra 0 warning; 6页离屏rc=0。未部署真机(留主会话)
2026-09-01 15:55:42 +08:00

166 lines
7.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* bench/degrade_sim.c — song_url_auto 全档降级链离线矩阵(§20,0 真实请求)。
*
* 驱动真实 core/ncm_api.c 的 ncm_api_song_url_auto_f(注入假 try_fn,按「可用下限」
* 模型应答:level <= avail_from 才给 url)。覆盖:
* ① desired × avail_from 全矩阵(9 档 × 9 档 + 全不可用):命中档 = min(desired, avail_from),
* 请求数 = desired - 命中 + 1(逐级试,不多不少);
* ② desired 越界钳制(DOLBY=16 → SURROUND;负数 → STANDARD);
* ③ url 在但 type 未知(如 aac)→ 跳过继续降;
* ④ type 缺失时按 url 扩展名收 flac;
* ⑤ out_level 回填值与命中档一致(「下首恢复用户档」由调用方重传 desired 保证,
* 链内不记忆——每次调用独立从 desired 起步)。
*
* 编译(宿主,ncm_app 目录下,先跑过 build_core.sh 产出 out/core-host/ 全部对象):
* gcc -O2 -Wall -Wextra -DJSMN_HEADER -Icore -Ivendor/jsmn \
* bench/degrade_sim.c out/core-host/全部.o \
* -Lout/mbedtls/host -lmbedtls -lmbedx509 -lmbedcrypto -o degrade_sim -lpthread -lm
* 运行:./degrade_sim (PASS/FAIL 退出码)
*/
#include "ncm.h"
#include <stdio.h>
#include <string.h>
static int g_fail = 0;
#define CHECK(cond, fmt, ...) do { \
if (cond) printf(" PASS " fmt "\n", ##__VA_ARGS__); \
else { printf(" FAIL " fmt "\n", ##__VA_ARGS__); g_fail = 1; } \
} while (0)
/* ---- 假逐档取址:level <= g_avail_from 给 url(type 可配),记录每档请求次数 ---- */
static int g_avail_from; /* 可用下限;-1=全档不可用 */
static const char *g_type = "mp3"; /* 应答 type(""=缺省看扩展名) */
static const char *g_resp_level = NULL; /* 非 NULL:响应 data[0].level 固定串(服务端静默降档模拟) */
static int g_calls[NCM_LEVEL_SURROUND + 1];
static int fake_try(const char *dev, const char *ck, const char *id, int level,
ncm_play_url_t *out)
{
(void)dev; (void)ck; (void)id;
if (level >= 0 && level <= NCM_LEVEL_SURROUND) g_calls[level]++;
memset(out, 0, sizeof *out);
if (level > g_avail_from) return NCM_OK; /* 该档不可用:url 空 */
snprintf(out->url, sizeof out->url, "http://sim/%lld.%s", (long long)12345,
(level >= NCM_LEVEL_LOSSLESS) ? "flac" : "mp3");
snprintf(out->type, sizeof out->type, "%s", g_type);
if (g_resp_level) snprintf(out->level, sizeof out->level, "%s", g_resp_level);
out->br = 320000;
out->size = 9000000;
return NCM_OK;
}
static int g_bad_type_from = -1; /* >=0: 该档以上给 aac(未知格式应跳过) */
static int fake_try_badtype(const char *dev, const char *ck, const char *id, int level,
ncm_play_url_t *out)
{
int r = fake_try(dev, ck, id, level, out);
if (r == NCM_OK && out->url[0] && level >= g_bad_type_from)
snprintf(out->type, sizeof out->type, "aac");
return r;
}
static void reset_calls(void) { memset(g_calls, 0, sizeof g_calls); }
static int total_calls(void)
{
int n = 0;
for (int i = 0; i <= NCM_LEVEL_SURROUND; i++) n += g_calls[i];
return n;
}
int main(void)
{
ncm_play_url_t u;
int eff;
printf("== 1) desired × avail_from 全矩阵 ==\n");
int cases = 0, bad = 0;
for (int desired = NCM_LEVEL_STANDARD; desired <= NCM_LEVEL_SURROUND; desired++) {
for (int avail = NCM_LEVEL_STANDARD; avail <= NCM_LEVEL_SURROUND; avail++) {
reset_calls();
g_avail_from = avail; g_type = "mp3";
int rc = ncm_api_song_url_auto_f("dev", "ck", "1", desired, &u, &eff, fake_try);
int want = (avail < desired) ? avail : desired; /* 期望命中 = min */
int ok = (rc == NCM_OK) && (eff == want) && u.url[0] &&
(total_calls() == desired - want + 1);
/* 高档(>命中)各恰好 1 次,低档不请求 */
for (int lv = want + 1; lv <= desired; lv++) ok = ok && (g_calls[lv] == 1);
for (int lv = 0; lv < want; lv++) ok = ok && (g_calls[lv] == 0);
cases++;
if (!ok) {
bad++;
printf(" FAIL desired=%d(%s) avail=%d -> rc=%d eff=%d calls=%d (want %d/%d)\n",
desired, ncm_api_level_str(desired), avail, rc, eff, total_calls(),
want, desired - want + 1);
g_fail = 1;
}
}
/* 全不可用 → NOTFOUND,且恰好 desired+1 次请求 */
reset_calls();
g_avail_from = -1; g_type = "mp3";
int rc = ncm_api_song_url_auto_f("dev", "ck", "1", desired, &u, &eff, fake_try);
int ok = (rc == NCM_ERR_NOTFOUND) && (eff == -1) &&
(total_calls() == desired - NCM_LEVEL_STANDARD + 1);
cases++;
if (!ok) {
bad++;
printf(" FAIL desired=%d all-unavail -> rc=%d eff=%d calls=%d\n",
desired, rc, eff, total_calls());
g_fail = 1;
}
}
CHECK(bad == 0, "matrix %d cases all pass (desired 9 x avail 9 + 全不可用 9)", cases);
printf("== 2) desired 越界钳制 ==\n");
reset_calls();
g_avail_from = NCM_LEVEL_SURROUND; g_type = "mp3";
CHECK(ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_DOLBY, &u, &eff, fake_try) == NCM_OK &&
eff == NCM_LEVEL_SURROUND && total_calls() == 1 && g_calls[NCM_LEVEL_SURROUND] == 1,
"desired=DOLBY(16) clamped to SURROUND (eff=%d calls=%d)", eff, total_calls());
reset_calls();
CHECK(ncm_api_song_url_auto_f("d", "c", "1", -7, &u, &eff, fake_try) == NCM_OK &&
eff == NCM_LEVEL_STANDARD && total_calls() == 1,
"desired=-7 clamped to STANDARD (eff=%d)", eff);
printf("== 3) 未知 type 跳过继续降 ==\n");
reset_calls();
g_avail_from = NCM_LEVEL_SURROUND; g_bad_type_from = NCM_LEVEL_HIRES;
/* hires 及以上 url 在但 type=aac → 跳过,落在 lossless */
CHECK(ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_SKY, &u, &eff, fake_try_badtype)
== NCM_OK && eff == NCM_LEVEL_LOSSLESS,
"aac above hires -> land lossless (eff=%d)", eff);
printf("== 4) type 缺失按 url 扩展名收 flac ==\n");
reset_calls();
g_avail_from = NCM_LEVEL_LOSSLESS; g_type = "";
CHECK(ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_LOSSLESS, &u, &eff, fake_try)
== NCM_OK && strcmp(u.type, "flac") == 0,
"type empty + .flac url -> type backfilled flac");
printf("== 5) out_level 每次独立(下首恢复用户档语义) ==\n");
reset_calls();
g_avail_from = NCM_LEVEL_STANDARD; g_type = "mp3";
ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_SURROUND, &u, &eff, fake_try);
int first_eff = eff;
reset_calls();
g_avail_from = NCM_LEVEL_SURROUND;
ncm_api_song_url_auto_f("d", "c", "2", NCM_LEVEL_SURROUND, &u, &eff, fake_try);
CHECK(first_eff == NCM_LEVEL_STANDARD && eff == NCM_LEVEL_SURROUND,
"prev degraded to standard, next song back at surround (eff %d -> %d)",
first_eff, eff);
printf("== 6) 响应 level 静默降档采信(data[0].level < 请求档) ==\n");
reset_calls();
g_avail_from = NCM_LEVEL_SURROUND; g_type = "flac"; g_resp_level = "exhigh";
CHECK(ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_HIRES, &u, &eff, fake_try) == NCM_OK &&
eff == NCM_LEVEL_EXHIGH,
"req hires, resp level=exhigh -> eff=exhigh (got %d)", eff);
reset_calls();
g_resp_level = "sky"; /* sky 族有歧义:不采信,保守取请求档 */
CHECK(ncm_api_song_url_auto_f("d", "c", "1", NCM_LEVEL_SURROUND, &u, &eff, fake_try) == NCM_OK &&
eff == NCM_LEVEL_SURROUND,
"req surround, resp level=sky (歧义) -> eff=surround (got %d)", eff);
g_resp_level = NULL;
printf(g_fail ? "== DEGRADE SIM: FAIL ==\n" : "== DEGRADE SIM: ALL PASS ==\n");
return g_fail;
}