Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/play/ipc_media.c
T
AskaEth 68ce46a5d8 fix(ncm §32): 内存瘦身修 OOM 被杀 —— 封面降采样(8.28MB→0.78MB) + 瞬态 rbuf 缩减 + 线程栈显式
真机 dmesg 实锤:OOM Kill ncm (score 460, anon-rss 26.9MB, 设备 56MB)。
画像:封面全分辨率 RGBA 8.28MB(1448x1448 原生解码驻留) 是最大可控块。

瘦身(按收益排序):
1. sdk meow_ui_img 新增纯增量 meow_img_downscale + cover.c cover_cap(≤480px)
   → 封面 8.28MB→0.78MB,-7.65MB(mem_sim 实测 PASS)。
2. ncm_api.c 瞬态响应 rbuf:song_url→64KB、lyric/song_detail/cloudsearch/user_playlist→256KB
   (detail/track_all 保 1MB)。
3. ncm_app_internal.h 新增 ncm_thread_create(显式 128KB 栈),替换全部 14 处 pthread_create。
4. 滑动窗口/meta 复核零回退(window_sim:608B/首,6000 首 280KB 恒定)。

验证:mem_sim 封面收益 PASS + host home/player 渲染 OK + stream_sim/player_flow_sim/
window_sim/search_sim/cache_evict_sim 全 PASS + test_ncm_core host+mipsel(qemu) PASS
+ build_ncm.sh -Wall -Wextra 0 warning。真机 scp 播放在跑确认(缓存曲,0 播放请求)。

新 ncm md5 44fa16fe(text 1015150/bss 1362500)。正式部署留主会话。
2026-09-02 03:43:42 +08:00

220 lines
7.3 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.
/* play/ipc_media.c — 媒体会话 IPC 最小客户端(SDK 尚无媒体封装,App 内用 meow_ipc.h 帧格式直连)。
*
* 连接:AF_UNIX SOCK_STREAM 到 MEOW_IPC_SOCK(缺省 /usr/data/meow_ipc.sock)。HELLO(app_id,
* MF_PLAYBACK) 注册为媒体源;之后**同一连接**周期发 MSG_META(~1s,进度随播放推进)并阻塞等
* launcher 下发 MSG_PLAYCTL(播放/暂停/上下曲/seek/toggle)。launcher 经该连接 fd 回发(meow_media.c
* send_playctl 用最近活跃会话的 fd)。无 socket/launcher 不跑 → 优雅跳过(打日志不崩)。
* 线程模型:独立线程(main 起了 meow_ui_run 阻塞;播放线程归 player_manager,本线程只做 IPC 收发)。
*/
#include "../ncm_app_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "meow_ipc.h"
static pthread_t g_thr;
static int g_thr_on;
static int g_quit;
static const char *sock_path(void)
{
const char *e = getenv("MEOW_IPC_SOCK");
return (e && e[0]) ? e : MEOW_IPC_PATH;
}
static int connect_path(const char *path)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return -1;
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, path, sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
return fd;
}
/* 发送一帧(头+负载)。 */
static int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC;
uint16_t ver = MEOW_IPC_VERSION;
memcpy(hdr + 0, &magic, 4);
memcpy(hdr + 4, &ver, 2);
memcpy(hdr + 6, &type, 2);
memcpy(hdr + 8, &req_id, 4);
memcpy(hdr + 12, &len, 4);
size_t off = 0;
while (off < sizeof hdr) {
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
if (w <= 0) return -1;
off += (size_t)w;
}
if (len && payload) {
off = 0;
while (off < len) {
ssize_t w = write(fd, (const uint8_t *)payload + off, len - off);
if (w <= 0) return -1;
off += (size_t)w;
}
}
return 0;
}
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
size_t off = 0;
while (off < sizeof hdr) {
ssize_t r = read(fd, hdr + off, sizeof hdr - off);
if (r <= 0) return -1;
off += (size_t)r;
}
uint32_t magic, len; uint16_t ver;
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2);
memcpy(&len, hdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
if (len > rcap) len = rcap;
off = 0;
while (off < len) {
ssize_t r = read(fd, rbuf + off, len - off);
if (r <= 0) return -1;
off += (size_t)r;
}
return (int)len;
}
/* 组并发送一条 META。 */
static void send_meta(int fd, uint32_t req_id)
{
m_meta_t mm;
memset(&mm, 0, sizeof mm);
ncm_strlcpy(mm.app_id, "dev.player.ncm", sizeof mm.app_id);
int idx = player_manager_playing_song();
pthread_mutex_lock(&g_play.mu);
int pos = (int)g_play.pos_ms;
int dur = (int)g_play.dur_ms;
int state = g_play.state;
int err = g_play.err;
pthread_mutex_unlock(&g_play.mu);
static char title[128], artist[128], album[128];
title[0] = artist[0] = album[0] = 0;
ncm_song_t sm;
if (idx >= 0 && player_manager_current_song(&sm) == 0) { /* 播放快照(窗口无关,§17) */
snprintf(title, sizeof title, "%s", sm.name);
snprintf(artist, sizeof artist, "%s", sm.art_name);
snprintf(album, sizeof album, "%s", sm.album_name);
}
snprintf(mm.title, sizeof mm.title, "%s", title);
snprintf(mm.artist, sizeof mm.artist, "%s", artist);
snprintf(mm.album, sizeof mm.album, "%s", album);
mm.pos_ms = (uint32_t)(pos < 0 ? 0 : pos);
mm.dur_ms = (uint32_t)(dur < 0 ? 0 : dur);
uint32_t st = 0;
if (err != 0) st = 0;
else if (state == NCM_PC_PLAYING) st = 1;
else if (state == NCM_PC_PAUSED) st = 2;
else if (state == NCM_PC_BUFFER) st = 3;
else st = 0;
mm.state = st;
/* 封面:若已落盘(cover 缓存文件)则提供路径 */
mm.flags = 0;
const char *cp = cover_current_path();
if (cp && cp[0]) {
snprintf(mm.artwork, sizeof mm.artwork, "%s", cp);
mm.flags |= MF_ART | MF_COVER;
}
if (lyric_count() > 0) mm.flags |= MF_LYRIC;
send_frame(fd, MSG_META, req_id, &mm, sizeof mm);
}
static void apply_playctl(const m_playctl_t *c)
{
switch (c->cmd) {
case NCM_CTL_PLAY: player_manager_pause(0); break;
case NCM_CTL_PAUSE: player_manager_pause(1); break;
case NCM_CTL_PREV: player_manager_next(-1); break;
case NCM_CTL_NEXT: player_manager_next(1); break;
case NCM_CTL_SEEK: player_manager_seek(c->seek_ms); break;
case NCM_CTL_TOGGLE:
/* 物理播放/暂停键(SDK 164 恒发 toggle,不再本地判 state):源是状态权威,
* 按当前态翻转(与播放页按钮 ui/player.c 同款判定)。非 PLAYING/PAUSED
* (停止/缓冲)时 pause() 内部按态过滤,天然 no-op 安全。 */
player_manager_pause(player_manager_state() == NCM_PC_PAUSED ? 0 : 1);
break;
default: break;
}
}
static void *ipc_thread(void *arg)
{
(void)arg;
int fd = connect_path(sock_path());
if (fd < 0) { meow_log(MEOW_LOG_WARN, "ipc: no launcher socket"); return NULL; }
m_hello_t h;
memset(&h, 0, sizeof h);
ncm_strlcpy(h.app_id, "dev.player.ncm", sizeof h.app_id);
h.version = 1;
h.flags = MF_PLAYBACK;
if (send_frame(fd, MSG_HELLO, 1, &h, sizeof h) != 0) { close(fd); return NULL; }
uint8_t hb[256];
uint16_t htype;
if (read_frame(fd, &htype, hb, sizeof hb) < 0) { close(fd); return NULL; }
send_meta(fd, 2); /* 首条注册媒体源(state=0 未播也登记,start 播后自动成源) */
uint64_t last_meta = 0;
while (!g_quit && !g_app_quit) {
struct pollfd p;
p.fd = fd; p.events = POLLIN;
int pr = poll(&p, 1, 200);
if (pr > 0 && (p.revents & POLLIN)) {
uint8_t rbuf[MEOW_IPC_MAX];
uint16_t type;
int n = read_frame(fd, &type, rbuf, sizeof rbuf);
if (n < 0) break;
if (type == MSG_PLAYCTL && n >= (int)sizeof(m_playctl_t)) {
m_playctl_t c;
memcpy(&c, rbuf, sizeof c);
apply_playctl(&c);
}
} else if (pr < 0 && errno != EINTR) {
break;
}
uint64_t now = meow_ui_time_ms();
if (now - last_meta >= 1000) {
send_meta(fd, (uint32_t)(now & 0x7FFFFFFF));
last_meta = now;
}
}
close(fd);
return NULL;
}
int ipc_media_start(void)
{
if (g_thr_on) return 0;
g_quit = 0;
if (ncm_thread_create(&g_thr, ipc_thread, NULL) != 0) return -1;
pthread_detach(g_thr);
g_thr_on = 1;
return 0;
}
void ipc_media_stop(void)
{
g_quit = 1;
/* 线程在 200ms poll 内退出(detach,不 join)*/
g_thr_on = 0;
}