这几天在为一个旧的项目写 mpv NPAPI 插件, 因为其它播放器对 RTSP 流支持不完够完善.

在调用 libmpv 的接口时, 发现了一些问题, 这个库给上层开发者提供的接口很不好用.

对后端播放状态的控制都是通过字符串命令来完成的

比如, 要暂停当前播放, 需要这样:

int flag = 0;
mpv_set_property(mpv_ctx, "pause", MPV_FORMAT_FLAG, &flag);

而要恢复播放, 就得这样:

int flag = 1;
mpv_set_property(mpv_ctx, "pause", MPV_FORMAT_FLAG, &flag);

其它更加复杂的操作, 需要给 mpv handle 传递多个参数. 而且参数的名称都是字符串.

这样的接口设计,有这些好处:

  • 库的开发者只需要提供很少量的接口, 就可以支持几乎所有操作.
  • 可以在别的进程中通过 domain socket 来给当前线程中的 mpv 实例传递控制指令.
  • 对于库的开发者来说, 当在新的版本中修改或者移除了某些接口时, 几乎不需要再手动修改 mpv/client.h

但是, 这种做法, 有非常严重的缺陷:

  • 对于库的使用者来说, 负担加重了很多. 明显的, 几乎要手动重新实现一遍 media player 的控制操作.
  • 编译器对字符串参数无能为力, 比如, 修改静音状态的指令是 ao-mute, 有一次写成了 oa-mute. 除了指令名容易写错完, mpv 的头文件里面也没有列出来它支持哪些指令操作, 每条指令需要什么格式的参数. 有的是需要 double, 别的可能需要 bool 或者字符串. 这些都需要去到 mpv 源代码里面查. 比较复杂的参数, 就需要使用 mpv_node 这个结构体了.
  • 别外, 在 client.h 头文件中, 声明了好些个结构体. 我比较推荐的做法是把这些都隐藏到库的内部, 只需要在头文件中声明一些函数接口就好了.

与之相反的, libvlc 对外提供的接口, 要优雅得多, 很统一整洁, 头文件里面的针对每个函数都有比较全的 注释说明. 几行代码初始化一个 vlc player 实例后就可以使用了.