fix(ncm): 歌单详情LoadFailed——track/all端点不存在(404)回退detail+分批song_detail已验证链路; 列表误触改按下锁定行+拖动抑制 (agent-227, 真机验证256首出曲) + perf(meowpanel): 屏幕帧整数定点DCT(无FPU软浮点3.96s→0.19s/帧,实测4-5fps)+前端链式轮询防堆积+实测fps显示 (agent-228)

ncm→73422166 已部署; meowpanel→f310a00e 已部署
This commit is contained in:
MeowOS committed 2026-08-31 17:40:04 +08:00
1 parent d394c73a7c
commit c041facd89
13 files changed
+447 -65

No files matched your search

+47 -28
View File
@@ -3,6 +3,12 @@
* 自包含:零第三方依赖,无 libm(DCT 用预计算余弦常数表),仅 malloc/realloc。
* 输入 16bpp RGB565 → YCbCr(4:2:0) → DCT → 量化 → zigzag → RLE/Huffman → entropy 字节填充。
* 供 MeowPanel 实时画面(GET /api/screen.jpeg)编码 fb0 帧。
*
* 性能(MeowOS=HiBy R3 Pro II,mipsel muslsf **无 FPU**,软浮点 double 极慢):
* 原始逐块 double 矩阵 DCT 每帧 480×720 编码约 3.96s,是实时画面掉帧到 5-6s/帧的根因。
* 本实现把 RGB→YCbCr 与 DCT/量化全部改为**整数定点**(FSCALE=13;BT.601 256 系数),
* 纯整数乘加(MIPS 原生 mult/add),实测 480×720 编码降至约 0.19s(约 20×),
* 质量/体积与 double 版 PSNR 等价(主机 480×720 高频测试图两者 PSNR 差 <0.1dB)。
*/
#include "mp_jpeg.h"
#include <stdlib.h>
@@ -168,40 +174,52 @@ static void build_q(const uint8_t *base, int quality, uint8_t *out) {
static int clamp255(int v) { if (v < 0) v = 0; if (v > 255) v = 255; return v; }
/* ---- 抓 8x8 样本(越界 clamp 到边缘) ---- */
/* 定点缩放:把 DCT 系数放大 2^FSCALE 后做纯整数乘加(MIPS 无 FPU,软浮点 double 极慢,
* 定点整数快一个量级)。两趟都 >> FSCALE,净缩放 2^(2*FSCALE),中间值在 int32 内(FSCALE=13 安全)。 */
#define FSCALE 13
static int mp_fix_round(int a) { /* 有符号四舍五入右移 */
return (a + (a >= 0 ? (1 << (FSCALE - 1)) : -(1 << (FSCALE - 1)))) >> FSCALE;
}
/* ---- 抓 8x8 样本(越界 clamp 到边缘;样本为中心化整数 -128..127) ---- */
static void grab8(const uint8_t *plane, int pstride, int pw, int ph,
int x0, int y0, double s[8][8]) {
int x0, int y0, int s[8][8]) {
for (int y = 0; y < 8; y++) {
int py = y0 + y; if (py >= ph) py = ph - 1;
const uint8_t *row = plane + (size_t)py * (size_t)pstride;
for (int x = 0; x < 8; x++) {
int px = x0 + x; if (px >= pw) px = pw - 1;
s[y][x] = (double)row[px] - 128.0;
s[y][x] = (int)row[px] - 128;
}
}
}
/* ---- 编码一个 8x8 块(DCT→量化→zigzag→RLE/Huffman) ---- */
static int encode_block(mj_bw *w, const double samp[8][8], const uint8_t *q,
/* ---- 编码一个 8x8 块(定点 DCT→量化→zigzag→RLE/Huffman) ---- */
static int encode_block(mj_bw *w, const int samp[8][8], const uint8_t *q,
const mj_hc *dc, const mj_hc *ac, int *prev_dc) {
static int ci[8][8];
static int ci_ready = 0;
if (!ci_ready) {
for (int u = 0; u < 8; u++)
for (int x = 0; x < 8; x++)
ci[u][x] = (int)(MP_COSP[u][x] * (1 << FSCALE) + 0.5);
ci_ready = 1;
}
int coef[64];
double R[8][8], F[8][8];
int R[8][8];
for (int y = 0; y < 8; y++)
for (int u = 0; u < 8; u++) {
double acc = 0;
for (int x = 0; x < 8; x++) acc += MP_COSP[u][x] * samp[y][x];
R[u][y] = acc;
int acc = 0;
for (int x = 0; x < 8; x++) acc += ci[u][x] * samp[y][x];
R[u][y] = mp_fix_round(acc);
}
for (int u = 0; u < 8; u++)
for (int v = 0; v < 8; v++) {
double acc = 0;
for (int y = 0; y < 8; y++) acc += MP_COSP[v][y] * R[u][y];
F[u][v] = acc;
}
for (int v = 0; v < 8; v++)
for (int u = 0; u < 8; u++) {
double c = F[u][v] / (double)q[v * 8 + u];
coef[v * 8 + u] = (int)(c + (c >= 0 ? 0.5 : -0.5));
int acc = 0;
for (int y = 0; y < 8; y++) acc += ci[v][y] * R[u][y];
int c = mp_fix_round(acc);
int Q = q[v * 8 + u];
coef[v * 8 + u] = (c + (c >= 0 ? Q / 2 : -Q / 2)) / Q;
}
int dcv = coef[0];
@@ -257,11 +275,11 @@ int mp_jpeg_encode_rgb565(const uint8_t *rgb565, int w, int h, int quality,
build_q(MQ_LUMA, quality, ql);
build_q(MQ_CHROMA, quality, qc);
/* RGB565 → Y 平面(全分辨率) + Cb/Cr 平面(2x2 平均) */
/* RGB565 → Y 平面(全分辨率) + Cb/Cr 平面(2x2 平均),全部整数定点(BT.601 近似)。
* Y=(77R+150G+29B)>>8;Cb=128+((-43R-85G+128B)>>8);Cr=128+((128R-107G-21B)>>8)。 */
for (int cy = 0; cy < h; cy += 2)
for (int cx = 0; cx < w; cx += 2) {
double cb = 0, cr = 0;
int n = 0;
int cb = 0, cr = 0, n = 0;
for (int dy = 0; dy < 2; dy++)
for (int dx = 0; dx < 2; dx++) {
int py = cy + dy, px = cx + dx;
@@ -272,14 +290,15 @@ int mp_jpeg_encode_rgb565(const uint8_t *rgb565, int w, int h, int quality,
int R = (r5 << 3) | (r5 >> 2);
int G = (g6 << 2) | (g6 >> 4);
int B = (b5 << 3) | (b5 >> 2);
Y[(size_t)py * (size_t)w + (size_t)px] = (uint8_t)clamp255((int)(0.299 * R + 0.587 * G + 0.114 * B + 0.5));
cb += -0.168736 * R - 0.331264 * G + 0.5 * B;
cr += 0.5 * R - 0.418688 * G - 0.081312 * B;
Y[(size_t)py * (size_t)w + (size_t)px] = (uint8_t)((77 * R + 150 * G + 29 * B) >> 8);
cb += (-43) * R - 85 * G + 128 * B;
cr += 128 * R - 107 * G - 21 * B;
n++;
}
size_t k = (size_t)(cy / 2) * (size_t)wc + (size_t)(cx / 2);
Cb[k] = (uint8_t)clamp255((int)(128.0 + cb / (n ? n : 1) + 0.5));
Cr[k] = (uint8_t)clamp255((int)(128.0 + cr / (n ? n : 1) + 0.5));
int dn = n ? n : 1;
Cb[k] = (uint8_t)clamp255(128 + ((cb / dn) >> 8));
Cr[k] = (uint8_t)clamp255(128 + ((cr / dn) >> 8));
}
mj_out o = {0};
@@ -331,17 +350,17 @@ int mp_jpeg_encode_rgb565(const uint8_t *rgb565, int w, int h, int quality,
for (int mx = 0; mx < mx_count; mx++) {
for (int by = 0; by < 2; by++)
for (int bx = 0; bx < 2; bx++) {
double s[8][8];
int s[8][8];
grab8(Y, w, w, h, mx * 16 + bx * 8, my * 16 + by * 8, s);
if (encode_block(&bb, s, ql, hdc_l, hac_l, &pd_y)) goto done;
}
{
double s[8][8];
int s[8][8];
grab8(Cb, wc, wc, hc, mx * 8, my * 8, s);
if (encode_block(&bb, s, qc, hdc_c, hac_c, &pd_cb)) goto done;
}
{
double s[8][8];
int s[8][8];
grab8(Cr, wc, wc, hc, mx * 8, my * 8, s);
if (encode_block(&bb, s, qc, hdc_c, hac_c, &pd_cr)) goto done;
}