Friday, 12 June 2020

Web VS FFmpeg VS duboku-downloader




注:由于测试涉及到连续剧画面,Malaysia, Vietnam, Thailand, South Korea, Indonesia, United States, Taiwan, Philippines, India 的用户需要其它地区的 VPN 才能观看此测试视频。

mpv player 能 skip broken m3u8 chunk, 但是 mplayer, vlc, windows media player 都有卡住的问题,mplayer (vlc 没问题, 忘了 windows media player) 甚至音频在卡住的情况下继续运行,所以卡完后音频比视频快了十秒。我写的下载器通过 pad(暴力循环 17 次(事实上 16 次就够了,不过 `ValueError` 速度超快,多一个 offset 不碍事)找出需要的 16 bytes 之一, 一旦成功 decrypt 即 break) 以及 remux ts to ts 解决了这一问题。我发现 start time 变成 0 而不是保持损失的视频时间四十多分钟才能在接下来的一次性 remux ts to mp4 变成正确的时间。 remux ts to ts 需要加新 muxer mpegts , 所以得重新编译 ffmpeg_minimal_ts_2_mp4.exe。

至于那个速度测试,与其说是我的快(纯 python 本来就不快), 还不如说是 ffmpeg 做冗余的工作导致慢。此测试已经尽可能让 ffmpeg 发挥默认(或许有隐藏选项可是我不懂,普通用户都不懂, 基本虚设) 最好的效能, 例如第一个 m3u8 是 output .ts 而不是 .mp4 以减少不必要的 remux。至于 python 则并非第一次跑 (第一次跑肯定有点慢, 但是现实中下载都是连续的)。 不过 ffmpeg 也好不到哪里去,不是第一次跑,也直接给 m3u8 链接 (现实中, 手动或扩展找 m3u8 链接都要花时间吧)。更新: 后来才发现先转 .mp4 比先转 .ts syscalls (还没算第二次转换)少, 只是不明白之前的随便测试比较是 .ts 比较快。无论如何, 速度差不了几秒, 不然我也不会用先转 .ts 来测试, 都是慢很多。

List syscalls by `strace -c`, duboku-downloader VS ffmpeg -i .m3u8 -c copy .mp4:




我没学 C 所以看不大懂(我小白 😶),不过先脑洞一下: 由于 FFmpeg 也必须支持 live streaming 的 m3u8 ,所以 FFmpeg 的方式可能没优化下载固定时长的 m3u8。

然后我也写了个 python 脚本分析了一下下载 .ts 速度的变化。只显示超过左边栏的差距能大于一秒的,然后右边则是当下累积的差距。能看出来,重新下载仍然是差距 ~37 秒 (最右下是  -37.6)。 此分析忽略时间格式有问题的 .ts, 包括第一个和最后二个两个,不过有其它数据足够分析了。此分析证明 FFmpeg 还是有快过我的地方,不过明显少于我快过它的地方 (右栏,~23 次我快 VS ~9 次它。 左栏则是 20 VS 12)。而且能重点分析为何第三个的 WW8CBwpZ.ts 快了 FFmpeg 近 4.7 秒, 连续则是近 7 秒。不过单体最快反而是 ffmpeg 的 5.7。



新版本的 duboku-downloader 解决了很多 bug,其中解决了 Windows 使用 ffmpeg 会出现烦人的黑窗口。首先认识了 shell=True完整的 string 可以 work。虽然 ffmpeg path 可以解决,可是 source/dest path 比较多工 (还要考虑盲目 concat 的安全性)。不过后来又发现 Windows 和 Linux 用回 list 没问题 ( Windows 用 ffmpeg full path), 只是 Linux 用 single string 需要加上 cwd=os.path.dirname(os.path.realpath(__file__)) 。也发现 Linux 不用 ffmpeg full path 会运行 sh -c ffmpeg ... 可是竟然没有引号 no wonder 上次失败 ! 再后来把 "ffmpeg_minimal_ts_2_mp4.exe" 嵌入 "独播库下载器.exe", 得改用 sys._MEIPASS 才能知道 pyinstaller 默认的 ffmpeg path。以前/开头试过 binaries 也不成功,后来才知道 pyinstaller .sepc 的很多东西要用 tuple 表达, 例如:  binaries=[ ('duboku_lib\\ffmpeg_minimal_ts_2_mp4.exe', 'duboku_lib') ],前者是 pyinstaller 找 ffmpeg_minimal_ts_2_mp4.exe 拿来 build 的路径, 后者则是 runtime 的路径 (也就是 sys._MEIPASS 的子目录)。用在 subprocess 的完整路径就需要 os.path.join(sys._MEIPASS, os.path.join('duboku_lib', ffmpeg_path) )。

项目链接: https://github.com/limkokhole/duboku-downloader

No comments:

Post a Comment