按 devdocs/plan/NCM_STREAM_PLAY.md §8 阶段3: - .part 断续传:中断留 .part,续传带 Range,206 追加 / 200 丢头段 / 416 删残留整首重下;URL 每次新拉 - LRU 防淘汰:evict/total_bytes 跳过 .tmp/.part(在播文件不被动过) - 失败重试:预缓冲前网络失败重试 1 次,退避 800ms,重试拿新签名 URL(风控:0 真实请求,未部署) - 顺手修:gb_to_bytes 返回 int 溢出致默认 10GB 上限失效 -> uint64_t - 验证:stream_sim(续传 PCM 金标准+全矩阵)/player_flow_sim(P9/P10/P11)/cache_evict_sim(case7/8)/test_ncm_core 宿主+qemu-mipsel 全 PASS;build_ncm.sh 0 warning;ncm_host 离屏正常 md5 ncm f8f316a1e6f3bdd8f6b5921fdbb3f3a2
83 lines
4.5 KiB
C
83 lines
4.5 KiB
C
/* 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);
|
||
/* 断点续传(阶段3,§5.2):path 已含既存前缀(.part),written 置为前缀字节数——
|
||
* 读端 read_pos=0 可立即读到已下前缀(秒开播),写端从 written 继续推进。 */
|
||
int stream_src_open_stream_at(stream_src_t **out, const char *path, uint64_t written);
|
||
/* 缓存命中路径:完整文件,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 */
|