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

+79
View File
@@ -0,0 +1,79 @@
/* play/stream_src.h — 边下边播共享流源(REPORT §21,方案 NCM_STREAM_PLAY A:写穿缓存)。
*
* 模型:下载线程把字节顺序写进 .tmp(ncm_cache 写者 write(2) 直写,不走 stdio 缓冲),
* 解码线程经本对象 pread 同一文件——落盘的文件本身就是下载缓冲,内存里无第二份拷贝。
* 写端每写完一块调用 stream_src_note_written() 推进可读边界并 broadcast 唤醒读端;
* 下载结束(完成/中断)stream_src_finish() 置 final;切歌/停止 stream_src_abort()。
*
* 读回调契约(实测红线 F7):stream_src_read_cb 严格 fread 语义——填满才返回,
* 真 EOF(final/failed/abort 且数据耗尽)才短读;否则 minimp3 把短读当 EOF 假播完。
* dr_flac 回调语义不同(返回实际读到的字节数):本对象同样先尽力填满再返回,
* 两种契约同时满足。
*
* 本层只依赖 pthread/pread/write,不依赖 core(保持 app/play 层自含)。
*/
#ifndef NCM_STREAM_SRC_H
#define NCM_STREAM_SRC_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stream_src stream_src_t;
/* seek 原点(与 drflac_seek_origin 同值,避免本头依赖 dr_flac.h)。 */
enum { STREAM_SRC_SEEK_SET = 0, STREAM_SRC_SEEK_CUR = 1, STREAM_SRC_SEEK_END = 2 };
/* 下载路径:tmp_path 已由 ncm_cache_open 创建(写端持有自己的写 fd);
* 本函数只开只读 fd(pread 与写端 write 无偏移冲突),written 从 0 起由写端推进。 */
int stream_src_open_stream(stream_src_t **out, const char *tmp_path);
/* 缓存命中路径:完整文件,written=final=size,read 永不阻塞(同一套读回调)。 */
int stream_src_open_static(stream_src_t **out, const char *path, uint64_t size);
void stream_src_close(stream_src_t *s);
/* ---- 写端(下载线程) ---- */
/* 数据已落盘(ncm_cache_write 成功)后调用:推进可读边界 + 唤醒读端。 */
void stream_src_note_written(stream_src_t *s, uint64_t n);
/* 下载结束:err==0=完成(final=written),否则中断(final=written + failed=err)。
* 之后 read_cb 数据耗尽即短读(真 EOF),解码自然结束于截断点(F5)。 */
void stream_src_finish(stream_src_t *s, int err);
/* 切歌/停止:唤醒所有等待,read 立即按 EOF 短读返回;下载回调应随后返回 NZ 中止 http。 */
void stream_src_abort(stream_src_t *s);
int stream_src_aborted(stream_src_t *s); /* 下载回调每块开头廉价检查 */
/* ---- 预缓冲 ---- */
/* 等 written >= need;final/failed/abort 提前返回。timeout_ms 为单次等待上限
* (调用方在循环里套切歌护栏)。返回 1=满足,0=超时,<0=已结束/失败/中止。 */
int stream_src_wait(stream_src_t *s, uint64_t need, int timeout_ms);
/* 同步 peek(pread,不动 read_pos):magic 探测用。off+n 超出 written 的部分不读,
* 返回实际读到字节数。 */
size_t stream_src_peek(stream_src_t *s, void *buf, size_t n, uint64_t off);
/* ---- minimp3 回调(MP3D_READ_CB / MP3D_SEEK_CB) ---- */
size_t stream_src_read_cb(void *buf, size_t size, void *ud);
int stream_src_seek_cb(uint64_t pos, void *ud);
/* ---- dr_flac 回调(drflac_read_proc / drflac_seek_proc / drflac_tell_proc) ---- */
size_t stream_src_drflac_read(void *ud, void *buf, size_t bytes_to_read);
int stream_src_drflac_seek(void *ud, int offset, int origin); /* origin=STREAM_SRC_SEEK_* */
int stream_src_drflac_tell(void *ud, int64_t *cursor);
/* ---- 统计 / 停滞联动 ---- */
/* underrun=read_cb 追上下载(无数据可给)的等待次数;wait_ms=累计等待 ms。 */
void stream_src_stats(stream_src_t *s, int *underruns, uint64_t *wait_ms);
/* 单次 read 等待 >1s 置位(播放线程下一轮对 audiod pause(1),恢复后 pause(0))。
* 取走即清零。 */
int stream_src_take_stalled(stream_src_t *s);
uint64_t stream_src_written(stream_src_t *s);
uint64_t stream_src_final(stream_src_t *s); /* 0=下载中;>0=最终大小(完成=全长,中断=截断点) */
int stream_src_failed(stream_src_t *s); /* 0=无错误;<0=下载/IO 错误码 */
#ifdef __cplusplus
}
#endif
#endif /* NCM_STREAM_SRC_H */