Dpdo 外部解码库 API(v1)

Dpdo 本体不打包、不链接任何第三方解码器。你自己放一个动态库(.so / .dll), 在播放器里选中它,Dpdo 就按下面这套 ABI 调用它 —— 解出来的是标准交错 float32 PCM。

为什么是这样

第三方解码器(尤其 ffmpeg 这类)的许可与专利责任在提供库的一方。Dpdo 只提供"装载 + 按能力分派"的机制: 库是你拿到的、你放进去的、你决定装不装。这与 VLC 等播放器的做法一致。

两种库,一个装载入口

形态说明Dpdo 侧
插件库(推荐)导出下表 7 个 dpdo_ext_* 函数,由库自己做容器解析与解码严格按 ABI 调用
裸 ffmpeg直接是 ffmpeg 的 .so(导出 avformat_open_input / avcodec_open2 等),没有 dpdo_ext_*自动识别,按 ffmpeg 老 API 直驱;库自带什么解码器就解什么

装载时先按插件 ABI 校验;不是插件就再按裸 ffmpeg 试。两条都失败时,日志会同时给出两条路各自的原因,例如:

player_cli: not a DPDO decoder plugin [ABI version mismatch: library reports 99, this build expects 1];
	            not a usable raw ffmpeg [missing symbol 'avformat_open_input']
	

装载方式

场景做法
命令行(Linux / Windows)DpdoPlayerEngine -ext-decoder /path/to/lib.so
关闭已装载的库DpdoPlayerEngine -no-ext-decoder
桌面 GUI设置 → 启用外部解码库 → 选择文件(同一份参数由 GUI 传下去)
Android / 鸿蒙设置 → 启用外部解码库 → 选择文件(应用会把它拷进自己的私有目录)

试装结论是三态,界面与日志如实显示:ok(插件 ABI)/ okraw(裸 ffmpeg)/ fail: 原因。

插件 ABI:7 个函数

头文件是唯一权威定义:public/dpdo_ext_decoder.h。C 链接、不加名字修饰(C++ 请包 extern "C")。

int          dpdo_ext_abi_version(void);                                  /* ① 版本 */
	const char  *dpdo_ext_codecs(void);                                       /* ② 能力 */
	void        *dpdo_ext_open(const char *path, int *channels,
	                           int *sample_rate, int *bits_per_sample,
	                           long long *total_frames);                      /* ③ 打开 */
	long long    dpdo_ext_read(void *handle, float *out, long long frames);   /* ④ 读 */
	int          dpdo_ext_seek(void *handle, long long frame);                /* ⑤ 定位 */
	void         dpdo_ext_close(void *handle);                                /* ⑥ 关闭 */
	const char  *dpdo_ext_last_error(void *handle);                           /* ⑦ 最近错误 */
	
#语义硬约束
①返回 DPDO_EXT_DECODER_ABI_V1(当前 = 1)不匹配 ⇒ 引擎立即 dlclose 并报错,不会把调用落到这个库上
②逗号分隔的小写能力名,如 "dts,ape,alac"字符串由库持有,进程存活期内必须有效;空串 = 什么都解不了,按失败处理
③成功返回句柄;四个出参必须全部写有效值失败返回 NULL,原因走 ⑦
④读最多 frames 帧到 out(交错 float32);返回实际帧数0 = 流结束;负值 = 出错;引擎不会请求超过 total_frames
⑤跳到第 frame 帧(0 起)返回 0 = 成功;允许近似,但之后的读必须从该点继续、不串音
⑥释放句柄每个成功的 open 恰好配一次 close
⑦可读错误串(库持有,不要 free);无错返回 ""open 失败时引擎没有句柄,会以 NULL 调用 ⇒ 必须支持库级错误并照样返回原因

输出约定

  • 交错 float32,取值 −1.0 … +1.0;一帧 = 每声道一个采样。
  • 整型 PCM 的归一化由库负责(与引擎内部 float 管线一致)。
  • 不改采样率、不做重采样:引擎按你报的 sample_rate 走后续链路。

裸 ffmpeg 的边界

  • 引擎不声明 ffmpeg 的结构体,按实测偏移读写(对应 ffmpeg 3.x 时代布局);布局对不上时会拒绝而不是硬猜。
  • 只有库自己带的解码器可用 —— 你的 .so 里没有 APE 解码器,就解不了 APE。界面文案因此不预设格式名单。
  • 定位(seek)走"重开容器 + 精确跳帧",不依赖 demuxer 的 av_seek_frame。
  • 容器头与内容冲突时以内容为准(典型:DTS-CD 的 .wav 头声明的是 PCM 16bit,那是假的)。

平台

平台库文件说明
Linux.so直接给绝对路径
Windows.dll同目录的依赖 DLL 会自动找(装载用 LOAD_WITH_ALTERED_SEARCH_PATH)
Android.so应用会把选中的库拷到私有目录 files/decoders/;换库要在设置里重新选一次
鸿蒙.so同上
iOS—系统不允许加载外来动态库,这条路走不通

自己验一遍你的库

  1. 装载:跑一次 DpdoPlayerEngine -ext-decoder <路径> -i <素材>,看启动几行里的结论 —— 成功会打印 external decoder library loaded: … (codecs: …) 或 raw ffmpeg loaded: …; 失败会打印两条路各自的原因(缺哪个符号 / ABI 版本不符)。
  2. 能解什么:让它打开一个该格式的文件,看会话参数里有没有 -ext-decoder <路径>(这是"库真的生效"的硬判据)。
  3. 解出来的 PCM 对不对:与参照实现(例如 ffmpeg -i x.wv -f f32le -)逐样点比对,期望差值为 0。
  4. 负例:截断文件必须明确失败,不允许静默补齐后当完整素材放出去。

许可与责任

  • Dpdo 不随包分发任何第三方解码库,安装包里没有它们的文件。
  • 库的许可(含 GPL / 专利)与合规责任在提供库的一方;请自行确认你有权使用与分发。
  • 我们不提供、不推荐、不代下载任何具体的第三方库。

变更记录

ABI 版本变更
v1首版:7 个符号;三态试装结果;last_error(NULL) 语义明确化