Friday 23 November 2018

Ubuntu build mplayer 药丸

想 debug mplayer, 所以就想在 Ubuntu 上 build 源代码, 如常 `apt source mplayer`, `./configure`, `make`, 结果停在错误信息:

"libmpcodecs/ad_ffmpeg.c:137:69: error: 'FF_INPUT_BUFFER_PADDING_SIZE' undeclared (first use in this function); did you mean 'AV_INPUT_BUFFER_PADDING_SIZE'?"

Ubuntu 的 package 源代码有时不是最新的 (Ubuntu 药丸) ,所以就 `svn checkout svn://svn.mplayerhq.hu/mplayer/trunk mplayer` 下载 svn 源代码:


你可以看见明显 FF 被 AV 替换了 的区别:


所以这个 make 毫无悬念过关 ad_ffmpeg.c:

可是停在新的错误信息:

./libvo/videodev_mjpeg.h:133:42: error: 'BASE_VIDIOCPRIVATE' undeclared (first use in this function); did you mean 'SIOCDEVPRIVATE'?

Google "BASE_VIDIOCPRIVATE"  可以找到源代码 "BASE_VIDIOCPRIVATE" 在 include/linux/videodev.h 头文件声明:


kdiff3 一下它与我 Ubuntu  的 include/linux/videodev.h, 可以看见明显只有左边有声明 BASE_VIDIOCPRIVATE


grep BASE 会发现其实有 videodev2.h, 那么 backup videodev.h 然后 videodev.h symlink videodev2.h 行不 ?



想多了, 理所当然很多问题, 而且 symlink 酱换岂不造成其它 package build 有问题 ?


而且尽可能用最新的内核开发文件才是王道, 所以此路不通。

既然错误信息提到 v4l 的问题, 想必 v4l 可能可以 configure:


可以看见是可以 disable 的, 不过要注意的是没必要 disable v4l2, 因为如上图在 Makefile 可以看见有问题的是 tvi_v4l.c  由 v4l1 选项负责, 与 v4l2 是分开的。
所以执行 `make distclean`; `./configure --disable-tv-v4l1`; make

最后就成功了, 看到 mplayer binary 有点小感动有没有 (づ。◕‿‿◕。)づ



可是如果要坚持 build `apt source mplayer` 的源代码如何 ? 如果要 build 旧版本的 mplayer 来学习公开过的安全漏洞如何 ? apt 的 changelog 也不是很准。

心动不如行动, 先 cd 回那个 apt 源代码目录。

这里先吹一下你可以重新跑 make 复制错误信息上面的 gcc 命令(有时没有, 如 make ffmpeg)或 make -n 直接复制第一行的 gcc 命令, 然后重新跑该 gcc 命令即可专注于那个错误信息, 一旦以后不再错误则表示解决了。 该 gcc 命令补上 -H 选项可以知道 include 的 hierarchy, 加上 konsole 的 regex search 可以一眼看出想要的 level。



然后 `grep -rn FF_INPUT_BUFFER_PADDING_SIZE ./*` 循环 grep 错误信息缺少 (FF_INPUT_BUFFER_PADDING_SIZE) 以及建议 did you mean (AV_INPUT_BUFFER_PADDING_SIZE) 的关键词:





可以看出没有 FF_INPUT_BUFFER_PADDING_SIZE 的声明但只有AV_INPUT_BUFFER_PADDING_SIZE。 也知道 APIChanges 文件提到了它们:


也就是说 FF_ 换成 AV_。 所以要想办法弄回 FF_。

所以就尝试用 git 的 ffmpeg 吧。如是以前下载的确保 `git checkout master`。 首先看见的是只有 AV_INPUT_BUFFER_PADDING_SIZE 而没有 FF_INPUT_BUFFER_PADDING_SIZE:

似乎跟之前一样。 所以就 git 搜索 FF_INPUT_BUFFER_PADDING_SIZE 声明是几时从源代码删除的。

这里搜索 FF_INPUT_BUFFER_PADDING_SIZE 而不是 AV_INPUT_BUFFER_PADDING_SIZE , 是因为 AV_ 的出现不表示 FF_ 不存在, 而且修改代码理应是逐渐增加的, AV_ 不一定得等 FF_ 删了才出现。

git 第一个, 发现 AV_ 通过 merge 才有的。 这也是为什么我用 `git log -m --first-parent -S` 而不是单纯的 `git log -S`, 避免掉进其它 branch 搜索而难以锁定精准的 commit, 只搜索 master parent 的 commit 上到下一条路搜索, 而不是又上又下。






git checkout 第一个:


由于 checkout 第一个没有结果, 所以继续 checkout 第二个。 一旦有结果, 就 checkout 第一个的 "上一个日期"。



让我用程序表达正确的步骤:

while (true) {
    if (search's_commit_#1 == 有) {。
        直接 checkout search commit #1, 因为它表示之前不是已删除而是未出现。
    } else if ( (search's_commit_#1 == 没有) && (search's_commit_#2 == 有) {
        checkout search commit #1 之前日期的一个 commit。
        break;
    } else ( (search's_commit_#1 == 没有) && (search's_commit_#2 == 没有) {
        继续找 next search's commit #3,回去上面的时候 #1 变成 #2, #2 变成 #3
        , 以此类推。
    }
}

简单来讲就是上到下顺序 grep 到有为止, 则取上一个 search(grep 没有的) 的上一个日期 commit (肯定 grep 有)。

根据此规则, 不能第一个 grep 不到就得到结果,因为第二个也可能没有哦。 因为表示已删除。

要注意的是 checkout 后记得 grep 一下确保是声明/定义而不是没用的 reference/comment 或其它同部分名。如 FF_INPUT_BUFFER_PADDING_SIZE_XXXXX 同部分名但其实不是我们要的, 不能当作 "有", 必须花更多时间找。 这时候可以考虑用二分搜索 narrow down。

不过这还没完, 检查声明是否有 #if 或 #ifdef 诸如此类的标识符:


有的话就要加上相关的
CFLAGS。 所以 cd 回去 mplayer, 重新 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX' ./configure; make` 一条龙。

可是还是有错误信息:


先检查是否有现成的, 有的话就要加上相关的 CFLAGS, 否则才用之前的那个checkout 规则。

然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV' ./configure; make` 一条龙:

可是有新的错误信息:

这也一样, 先检查是否有现成的, 有的话就要加上相关的 CFLAGS, 否则才用之前的那个checkout 规则。

然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure; make` 一条龙:

 可是还是有错误信息:


lol, 这不就是开头 svn 的 'BASE_VIDIOCPRIVATE' 错误信息吗 ? 

如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙:

`Makefile:2: ffbuild/config.mak: No such file or directory`

先查找那个 directory  是几时新建的:


挺新的, 所以要 checkout 新建之前的一个 commit:


 然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙, 不需要特地丢掉之前的 define, 有问题再说:



 还是有问题,  'fatal error: libavformat/protocol_list.c: No such file or directory`根本没这个文件:


svn mplayer开下上帝视角才懂 mplayer configure 新建的, walau:
 


 编译 ffmpeg 必须先编译 mplayer ? lol

换个角度想想, 也是对啦, 既然找不到就表示可以找出现 #include "libavformat/protocol_list.c" 之前的版本, 且可以更加贴近 apt 的真正 ffmpeg 版本。


这里提醒一下, git 有时需要  --follow 才能跟踪已改目录名字的文件。

 然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙:



又有错误 `fatal error: AudioToolbox/AudioToolbox.h: No such file or directory`

同上的解决方式


然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙:

搞笑, 这次到 video:  `fatal error: VideoToolbox/VideoToolbox.h: No such file or directory`

然后 cd 回去 mplayer,
如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙:

仍然有错误 `undefined reference to `ff_http_averror'`

已经是最后的 build 所以不应该靠 git。 把注意力放在 undefined reference , 表示找不到的函数名 ff_http_averror 以及出现在文件名 rtsp.c, 而不需要去理会 In function `ff_rtsp_averror' 表示什么函数名使用该上面的函数。

如上图所示, 函数定义在 http.c (http.h 可以忽略), 也就是编译后的 http.o, 可是 http.o 并没有出现在 rtsp.o (使用该函数的文件是 rtsp.c) 的后方。 所以用 vim (v alias)把 http.o 加入所有 rtsp.o 的后方。 上图所示只出现在 libavformat/Makefile, 所以只需要改这个文件。

同理, rtpproto.o 也是一样做法。放在所有的 rtsp.o 后面即可。

至于 rtspdec.c 唯一一行的 rtspdec.o 已是与 rtsp.o 重合, 所以不需要再修改。


接着再看 mss2.c 和 vc1.c, 也是同理, 区别只在于它的 Makefile 是在 libavcodec 目录而不是 libavformat 目录:



然后 cd 回去 mplayer,
如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make` 一条龙 (可以使用 `make VERBOSE=1` 更获得详细的 log):

有新的错误 `undefined reference to `ff_udp_set_remote_url'`

这次换主角了, 不再是 rtsp.c 而是 rtpproto.c, 不过仍然如常修改:


以及 libavcodec 目录, 不过与之前不同的是需要前缀 x86/ 目录:


然后 cd 回去 mplayer, 如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make VERBOSE=1` 一条龙:



哎, 这次错误异常多, `undefined reference to `ff_vc1_v_loop_filter8_mmxext'`

不过仍然实行之前的方式:


如常, 只不过 grep 要确保 grep x86 目录。 .asm 与 .c 一样换成 .o:


然而你会发现很奇怪的现象, 有 4 个是指向原本的文件 vc1dsp_init.c:


改变 grep, 可以看出压根没有其它真正的定义却直接使用:


必须修改它们成注释:


如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make VERBOSE=1` 一条龙:

grep 可以发现只有一个说明在 .asm 定义, 其它只是照跟。 如常修改:


这样一来其实之前那个不需要注释也行, 也是 .asm 分别定义 8x8, 4x8, 8x4, 4x4:


网上的一些基本解释:


如常 `make distclean; CFLAGS='-DFF_API_WITHOUT_PREFIX -DFF_API_DEBUG_MV -DFF_API_MOTION_EST' ./configure --disable-tv-v4l1; make VERBOSE=1` 一条龙:


有点小感动有没有 (づ。◕‿‿◕。)づ