分析

直到前几天我通过Google网易云音乐播放flac时的报错信息[00007f9b30003c50] prefetch stream error: unimplemented query (264) in control发现了下面的文章

https://blog.duama.top/2019/03/03/解决网易云音乐Linux版无法播放无损/

我也通过一番抓包并浏览VLC源码渐渐了解了原因; 原来, Linux版网易云音乐通过调用后端api获取音频文件URL, 然后就直接交给VLC了, VLC优先通过HTTP Header中的Content-Type获取文件类型(mime-type), 再根据mime-type选择decoder; 问题就出在这里, 当音频为flac时, 上面提到的音频文件URL获取的HTTP Header中的Content-Typeaudio/mpeg, 这是给mp3用的, flac文件正确的mime-type应该是audio/flac; 重现一下网易云音乐播放无损音乐的流程, 网易云音乐获取FLAC音频文件URL->VLC通过Content-Type获取文件类型为audio/mpeg->VLC选择mpeg解码器解码flac文件->VLC解码失败->网易云音乐播放失败.

知道了原因, 我们就可以开始解决它了, 上面提到的文章说是用Privoxy更改Content-Type(HTTP劫持)值为audio/flac, 然后设置http_proxy环境变量; 但不知道为什么, 我通过这种方式并不能截取到音频文件URL的HTTP流; 此外, 我个人也不希望就为了一个桌面应用在后台始终运行Privoxy; 于是我尝试寻找其他解决方法.

解决方案

在vlc源代码modules目录下运行grep -r "Content-Type", 发现了VLC获取"Content-Type"的函数调用

access/http/resource.c:    const char *type = vlc_http_msg_get_header(res->response, "Content-Type");

进入代码, 发现上面res的结构体有一个path的变量, 那么问题就简单了, 只要判断res->path后缀为flac, 那么就把*type变量值改为"audio/flac"就行了; 我在gist上创建了PATCH和可用的PKGBUILD文件

展开查看(Gist Embed)

这里有两个解决方法

  1. 应用PATCH后编译安装, 全局生效(云音乐v1.2.1失效)
  2. 应用PATCH编译后设置 LD_LIBRARY_PATH=<编译后VLC的lib目录>

应用PATCH, 编译

安装flac mpg123 libmpeg2 lua libmad libpulse alsa-libjack 的devel包及其他必要依赖, 下载vlc v3源码和ncm.patch

$ cd vlc
$ patch -p1 < <到patch文件的路径>
patching file modules/access/http/resource.c
$ ./configure  \
      --prefix=/usr  \
      --disable-rpath \
      --enable-mpg123 \
      --enable-flac \
      --enable-libmpeg2 \
      --disable-avcodec \
      --disable-swscale \
      --disable-a52
...
$ make -j$(nproc)
# 安装到用户目录,不覆盖系统vlc
$ make DESTDIR=$HOME/.local/share/vlc-patching install

如果你不想编译的话,可以下载我的预编译包适用用于当前Arch Linux, 其他Linux分布版可能会有库依赖缺失的问题

设置LD_LIBRARY_PATH运行网易云音乐

$ env LD_LIBRARY_PATH=$HOME/.local/share/vlc-patching/usr/lib netease-cloud-music

如果用的是我的预编译包, 上面命令中的$HOME/.local/share/vlc-patching/改成解压后的目录路径

也可以把网易云音乐desktop文件的Exec=netease-cloud-music %U如上更改

[Desktop Entry]
Categories=AudioVideo;Player;
Comment=网易云音乐
Icon=netease-cloud-music
Exec=env LD_LIBRARY_PATH=$HOME/.local/share/vlc-patching/usr/lib XDG_CURRENT_DESKTOP=DDE netease-cloud-music %U

更新 2019-05-27

最近更新的网易云音乐1.2.1把所有依赖都直接打包进了包里,所以它的/usr/bin里的netease-cloud-music是一个bash脚本,也使用了LD_LI­BRARY_­PATH来加载自带的依赖,但是有个问题,请看netease-cloud-music.bash:

#!/bin/sh
HERE=$(dirname "$(readlink -f "${0}")")
export LD_LIBRARY_PATH="${HERE}"/libs
export QT_PLUGIN_PATH="${HERE}"/plugins 
export QT_QPA_PLATFORM_PLUGIN_PATH="${HERE}"/plugins/platforms
exec "${HERE}"/netease-cloud-music $@

可以看到这里把 LD_LIBRARY_PATH 覆盖了,这样的话我们声明的LD_LIBRARY_PATH就无效了,于是无法播放flac的问题再次出现.解决方法是改成如下

...
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"${HERE}"/libs
...

个人推荐复制此脚本到用户目录下,并直接将脚本中的 LD_LIBRARY_PATH 改成最终的值,并复制修改网易云音乐的 desktop 到用户目录下( ~/.local/share/applications/ )

然后。。。,就没有然后了,直接点击网易云音乐进入即可。