feat(ncm §21): 边下边播阶段1+2——stream_src流式源+decoder双路open_cb+下载线程预缓冲/stall联动+P2 HTTP空闲超时+cache fd写者+stream_sim全矩阵PCM金标准(0真实请求,未部署)

This commit is contained in:
MeowOS committed 2026-09-01 18:23:17 +08:00
1 parent dbee37e1d6
commit 9e25db77cc
18 files changed
+1335 -136

No files matched your search

+29 -12
View File
@@ -4,6 +4,8 @@
#include "ncm_store.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@@ -95,7 +97,7 @@ int ncm_cache_lookup(const ncm_cache_t *c, const char *id, const char *ext,
struct ncm_cache_writer {
char final_path[NCM_CACHE_DIR_MAX + 64];
char tmp_path[NCM_CACHE_DIR_MAX + 80];
FILE *fp;
int fd; /* 写 fd(write(2) 直写,§21:不走 stdio 缓冲,字节对读端即时可见) */
uint64_t nwritten;
int failed;
};
@@ -105,23 +107,39 @@ int ncm_cache_open(const ncm_cache_t *c, const char *id, const char *ext,
if (!c || !id || !w_out) return NCM_ERR_ARG;
ncm_cache_writer_t *w = (ncm_cache_writer_t *)calloc(1, sizeof *w);
if (!w) return NCM_ERR_OOM;
w->fd = -1;
if (ncm_cache_path(c, id, ext, w->final_path, sizeof w->final_path) != 0) {
free(w); return NCM_ERR_FULL;
}
snprintf(w->tmp_path, sizeof w->tmp_path, "%s.tmp", w->final_path);
w->fp = fopen(w->tmp_path, "wb");
if (!w->fp) { free(w); return NCM_ERR_IO; }
w->fd = open(w->tmp_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (w->fd < 0) { free(w); return NCM_ERR_IO; }
*w_out = w;
return NCM_OK;
}
/* 写端用 write(2) 直写(§21):旧版 fwrite 有 4-8KB stdio 缓冲,字节对边下边播的
* pread 读端不可见;直写后每块落盘即推进可读边界。无用户态缓冲,fsync 只在 commit 一次。 */
int ncm_cache_write(ncm_cache_writer_t *w, const unsigned char *data, size_t len) {
if (!w || !w->fp || w->failed) return NCM_ERR_ARG;
if (fwrite(data, 1, len, w->fp) != len) { w->failed = 1; return NCM_ERR_IO; }
if (!w || w->fd < 0 || w->failed) return NCM_ERR_ARG;
size_t off = 0;
while (off < len) {
ssize_t n = write(w->fd, data + off, len - off);
if (n < 0) {
if (errno == EINTR) continue;
w->failed = 1; return NCM_ERR_IO;
}
off += (size_t)n;
}
w->nwritten += len;
return NCM_OK;
}
/* 写者 .tmp 路径(边下边播:解码侧用它开只读 fd 建 stream_src;只读,勿改)。 */
const char *ncm_cache_writer_tmp_path(const ncm_cache_writer_t *w) {
return w ? w->tmp_path : "";
}
static int cmp_mtime_asc(const void *a, const void *b) {
const entry_t *x = (const entry_t *)a, *y = (const entry_t *)b;
if (x->mtime < y->mtime) return -1;
@@ -169,15 +187,14 @@ int ncm_cache_evict(const ncm_cache_t *c) {
int ncm_cache_commit(ncm_cache_writer_t *w) {
if (!w) return NCM_ERR_ARG;
int rc = NCM_OK;
if (w->failed || !w->fp) rc = NCM_ERR_IO;
else if (fflush(w->fp) != 0) rc = NCM_ERR_IO;
else if (fsync(fileno(w->fp)) != 0) rc = NCM_ERR_IO;
else if (fclose(w->fp) != 0) rc = NCM_ERR_IO;
if (w->failed || w->fd < 0) rc = NCM_ERR_IO;
else if (fsync(w->fd) != 0) rc = NCM_ERR_IO;
else if (close(w->fd) != 0) { w->fd = -1; rc = NCM_ERR_IO; }
else {
w->fp = NULL;
w->fd = -1;
if (rename(w->tmp_path, w->final_path) != 0) { unlink(w->tmp_path); rc = NCM_ERR_IO; }
}
if (w->fp) { fclose(w->fp); w->fp = NULL; }
if (w->fd >= 0) { close(w->fd); w->fd = -1; }
if (rc == NCM_OK) {
/* 刷新 mtime(= 最近访问)*/
utime(w->final_path, NULL);
@@ -188,7 +205,7 @@ int ncm_cache_commit(ncm_cache_writer_t *w) {
int ncm_cache_abort(ncm_cache_writer_t *w) {
if (!w) return NCM_ERR_ARG;
if (w->fp) { fclose(w->fp); w->fp = NULL; }
if (w->fd >= 0) { close(w->fd); w->fd = -1; }
unlink(w->tmp_path);
free(w);
return NCM_OK;
+4 -1
View File
@@ -43,13 +43,16 @@ int ncm_cache_path(const ncm_cache_t *c, const char *id, const char *ext,
int ncm_cache_lookup(const ncm_cache_t *c, const char *id, const char *ext,
char *out, size_t out_sz);
/* 流式写入句柄:open 建 temp;write 累积;commit 落盘+刷新LRU+触发淘汰;abort 丢弃。 */
/* 流式写入句柄:open 建 temp;write 累积(write(2) 直写无 stdio 缓冲,§21 边下边播
* 读端即时可见);commit 落盘+刷新LRU+触发淘汰;abort 丢弃。 */
typedef struct ncm_cache_writer ncm_cache_writer_t;
int ncm_cache_open(const ncm_cache_t *c, const char *id, const char *ext,
ncm_cache_writer_t **w);
int ncm_cache_write(ncm_cache_writer_t *w, const unsigned char *data, size_t len);
int ncm_cache_commit(ncm_cache_writer_t *w); /* win; 返回 0=成功 */
int ncm_cache_abort(ncm_cache_writer_t *w);
/* 写者 .tmp 路径(只读):边下边播解码侧用它开只读 fd 建 stream_src。 */
const char *ncm_cache_writer_tmp_path(const ncm_cache_writer_t *w);
/* 当前缓存总字节(扫描目录) */
uint64_t ncm_cache_total_bytes(const ncm_cache_t *c);
+12 -2
View File
@@ -328,8 +328,15 @@ static int http_run(const ncm_http_req_t *req, sink_t *sink, ncm_http_resp_t *re
if (parse_url(req->url, &url) != 0) return NCM_ERR_ARG;
if (strcmp(url.scheme, "https") != 0 && strcmp(url.scheme, "http") != 0) return NCM_ERR_ARG;
/* 超时语义(P2 改造,REPORT §21):连接/握手/请求发送/响应头 保留绝对总上限 15s;
* 响应体阶段改**空闲超时**——每成功读到字节即顺延,15s 无字节才判超时。
* 旧实现 timeout_ms=30000 是全程绝对 deadline,整首下载超 30s 必 abort(慢网暗雷);
* 边下边播下载持续整曲时长,必须先改为空闲语义。 */
uint32_t tmo = req->timeout_ms ? req->timeout_ms : 15000;
if (tmo > 15000) tmo = 15000; /* 绝对段统一钳到 15s */
int64_t deadline = ncm_monotonic_ms() + (int64_t)tmo; /* 截止时刻用单调钟,避免 NTP 校时跳变 */
int64_t body_deadline = 0; /* hdr_done 后启用:每收字节顺延 15s */
#define NCM_HTTP_BODY_IDLE_MS 15000
mbedtls_net_context net;
@@ -467,10 +474,13 @@ static int http_run(const ncm_http_req_t *req, sink_t *sink, ncm_http_resp_t *re
if (body_feed(&bst, tmp, n, sink) < 0) { ret = NCM_ERR_JSON; goto done; }
}
if (sink->overflow || bst.st == BS_DONE) break;
if (hdr_done) body_deadline = ncm_monotonic_ms() + NCM_HTTP_BODY_IDLE_MS; /* 有字节→顺延(P2 空闲超时) */
} else if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
if ((ret = wait_io(&net, MBEDTLS_NET_POLL_READ, deadline)) != 0) { if (ret==MBEDTLS_ERR_SSL_TIMEOUT) ret=NCM_ERR_TIMEOUT; goto done; }
int64_t dl = hdr_done ? body_deadline : deadline; /* 响应体=空闲超时;此前=绝对上限 */
if ((ret = wait_io(&net, MBEDTLS_NET_POLL_READ, dl)) != 0) { if (ret==MBEDTLS_ERR_SSL_TIMEOUT) ret=NCM_ERR_TIMEOUT; goto done; }
} else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
if ((ret = wait_io(&net, MBEDTLS_NET_POLL_WRITE, deadline)) != 0) goto done;
int64_t dl = hdr_done ? body_deadline : deadline;
if ((ret = wait_io(&net, MBEDTLS_NET_POLL_WRITE, dl)) != 0) goto done;
} else if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
break; /* 对端关闭 */
} else {
+2 -1
View File
@@ -36,7 +36,8 @@ typedef struct ncm_http_req {
const char *body; /* 表单体,可为 NULL */
size_t body_len; /* 0 表示 strlen(body) */
const char *extra_hdrs; /* 附加头(完整 CRLF 行),可为 NULL */
uint32_t timeout_ms; /* 整体超时(ms);0=默认 15000 */
uint32_t timeout_ms; /* 连接/握手/请求/响应头的绝对总上限(ms,>15s 钳到 15s);0=默认 15000。
* 响应体阶段不受此限:改空闲超时,每收到字节顺延,15s 无字节判超时(P2,§21)。 */
int verify_ca; /* 1=校验CA,0=跳过(仅调试) */
const char *ca_path; /* 指定 CA bundle 路径;NULL=系统默认 */
} ncm_http_req_t;