写一个 Android 本地音乐播放器的心路历程

2019-10-08 11:58:09 +08:00
 novato

开始的想法很简单,就是能通过 wifi 传 mp3 到手机上播放,有单曲循环、顺序播放、目录循环什么的就行了。客户端应不需装任何软件,就用浏览器传文件。
但市面上好像没有这种应用,不是要数据线拷文件,就是要在线播放它提供的歌曲,胡里花哨的一大堆不需要的功能,还几十、上百兆的。
那我就着手自己写一个吧,首先要一个 android http server,请参考 [用一个安卓 app 实验全栈开发] ,这个有了之后又要加入文件管理功能,比如:创建新文件夹、移动、删除、重命名等。把这个简单的文件管理界面加上后,再加上播放器相关的:单曲播放、单曲循环、顺序播放、目录循环。因为这是用浏览器的<audio>标签播放,所以局域网内的其它人也可以同时播放你手机上的音乐。
那不是可以当作文件服务器用吗?干嘛限制只能上传 mp3 ?所以要让它可以上传十几个 G 的蓝光电影,或几百兆的 office 安装包。别人通过浏览器可以下载(或在线播放)你手机上的文件。好了,这些又有了之后感觉不太过瘾。
既然整个 boost asio 都在上面,顺便也写个 socks5 代理吧,让别人可以通过你手机代理上网,如果你手机有 vpn 账号的话还是有点用的。

目前所有的一切还在局域网内,要让它上广域网,界面是 vuejs 写的,运行在安卓 webview 里,我觉得应该把 webrtc 加上,webrtc 需要个 signaling server,然后我就用 nodejs 写了个 signaling server 做成了 docker 镜像(包含 stun/turn server ),随便在哪个有公网 ip 的服务器上 run 起来,不需要域名。这个 app 里再加上个“服务器配置界面”,可以配置多个服务器,就像 wow 的不同世界服一样,但要让它同时与不同服务器的玩家交互,就要有个 id 来标识它,就用手机硬件序列号的 hash 做 id 吧。比如玩家 a 同时在 s1,s2 两台服务器上出现的话,玩家 b 也在这两台服务器,那么 a 与 b 只会建立一个 webrtc 连接,因为同一 id 的玩家已有连接的话就不会再建立了。

流程是这样的:玩家打开 app,就会同时建立 N 个 websocket 连接到他配置的服务器去,配置了几个就连几个,如果没有一个服务器的话,那他就完全在自己局域网内当文件服务器用,每个公网服务器会随机选最多 100 个其它玩家的 id 发给它——通过加密后 websocket 连接(就像 https ),然后这个安卓 app 会把该服务器当作 signaling server,与这些玩家建立直接连接——其后与这些玩家的交互就不再经过任何服务器了。所以我又加了几个界面:附近玩家列表(直连的),私聊界面,附近频道,世界频道,及个人信息配置界面:昵称、头像、签名,是否允许加好友,是否允许音频 /视频聊天,是否允许被当作代理(这个后面会解释)。
私聊界面就类似微信的,可以发语音、文字、图片、文件什么的,还可以申请语音 /视频聊天,及请求代理。有的手机浏览器版本太低,建立实时音频 /视频聊天会失败,我个人测试用一加 5t 与华为平板可以视频聊天,但是与 nexus 6p 则会失败,可能要 Android7 以上的 webview 才行吧。不过这个不重要了,因为可以发录音文件嘛,就像微信那样按下说话,松开发送那种——而且还可以在附近频道群发。如果有人发垃圾消息可以屏蔽他,屏蔽后会与他断开直连,并不再接收其世界频道消息;有些人也可以加为好友,好友列表里的玩家会被优先直连,所有玩家不需要注册,他的手机就是他的 id,除非换手机。

到目前为止还没什么有创意的。上面说了每个玩家手机上都有个 socks5 代理,如果让别的玩家做代理服务器上网呢?
这个问题困扰了我许久,最初想用 udp 打洞穿透,然后 tcp 走 udp,但据说有些手机网 ISP 会屏蔽 udp,而且这个内 /外网的 ip 建立连接还要像 ICE 那样处理,相当于是把 webrtc 的功能再实现一遍,看了几篇 rfc 文档,觉得这个不是短期能做的。开始是想让 C++层直接穿透直连,因为 tcp 的监听也是在 C++做的,都放在一起做感觉会简单点,那时把那个udt(不是 udp )库都已编译成安卓版了,这个是可靠传输的 udp C++库,试了下感觉它那个接口跟 asio 不太兼容,明明用 C++写的,还非得要搞成 BSD socket 那样 C 接口,就放弃了。况且即使这个能用也还要实现几个 rfc 文档。所以就整个放弃 C++层穿透了。
webview 里的 webrtc 都已经直连了为什么不用呢?开始考虑的是 C++服务是运行在 Android Service 里,而 Webview 是运行在 Activity 里,service 可以长期运行,activity 切换到后台其中的代码执行就会被中断。所以又去查了下什么情况下 webview 里的 websocket 不会被中断。这篇文章说了个方法,试了一下,遗憾的是它需要用户手工开启特殊权限,而且允许这个特殊权限的接口在以后的 android 版本里还可能会变化。
算了,就直接用 Activity 里的 webview 里的 webrtc 吧。我把本地 socks5 的监听端口号+100,开另一个 tcp 监听(我称作远程 socks5 代理端口),当这个端口有连接过来,C++层会通过本地 websocket 询问 webview 层“是否有附近玩家同意作为代理”,有的话则通过 webrtc 通道(直连通道)通知对端的 webview 再通过它本地的 websocket 通知它的 C++层建立与它自身 socks5 的 tcp 连接……。说起来太拗口了,图示如下:

最终这样实现了,自己测试了一下效果还行,如果打开网站去查 ip 的话,会显示别人手机的 ip——为了测这个还买了个 XX 云 1 核 /1G/1M 的服务器自己搭了个 vpn 用另一台手机连接测试。因为没什么人脉,都是自己搞。
后面还想把 bt 下载也加进去,是用 webtorrent ( js 库)呢?还是用 libtorrent ( C++库)?我是倾向用 webtorrent,因为用 js 写起来简单些,结果一测根本就不支持老一点手机的 webview,就像那个视频聊天一样。

算了,等以后大家换几代手机再说吧。回想当初不是只写个音乐播放器吗,先到这吧,有点扯远了。

App 下载( 5.3M )

默认加了一个测试服地址——1 核 1G1 兆的服务器。你也可以自己起服务,然后让自己的圈内人都配置这个地址
signaling server 的代码地址,就一百多行 nodejs 代码

https://github.com/novice79/ss

1392 次点击
所在节点    分享创造
12 条回复
duwuyan
2019-10-08 15:06:51 +08:00
佩服
bigbigpeng3
2019-10-08 15:15:33 +08:00
楼主很棒啊,其实我也写过类似这种的 Android 文件服务器,可以直接通过网页查看 Android 手机内的所有文件,支持直接播放。这样 Android 就是一个移动 wifi U 盘了。目前没有发现做的比较好的 App。很多都是基于 PC 的。其实总体来说有点像百度云盘,或者是文件管理器 web 版,只有局域网能访问到。
Spoter
2019-10-08 15:33:17 +08:00
nb
knva
2019-10-08 16:37:08 +08:00
所以你最后写了个 av 共享播放器?
ciaoly
2019-10-08 16:44:45 +08:00
楼主好优秀吖🙊面对个性化需求,像我这种懒狗都是去找现成的 app 的。

离线播放器有 腾讯轻听,GitHub 有 ListenerMusicPlayer ;
WiFi 传文件(基于 HTTP 协议)有 XDA 的 Mixplorer、FastFileTransfer、chrome 应用“WebServer for chrome”;
安卓端的 Web server 有“HTTP server powered by Apache”;
倒是代理工具一直没找到🌚
hqweay
2019-10-08 18:45:19 +08:00
楼主这心理历程太真实了...等等,我最开始想干嘛来着:-D
zhw2590582
2019-10-09 09:01:16 +08:00
佩服,假如 UI 再漂亮点的话就更好了
337136897
2019-10-09 10:07:15 +08:00
恕我直言,你的 UI 弄得好丑,好像 10 的 UI
novato
2019-10-09 17:09:41 +08:00
@337136897 我承认,其实里面的代码也很垃圾。但总不能因为顾虑这顾虑那就不做了吧。
你看我的名字:novato (西班牙语)== novice (英语)== 小白(中文)——新手总是要起步的嘛
hkyyx
2019-10-09 17:30:05 +08:00
来个设计师小哥哥给楼主的设计一套 UI 啊
novato
2019-10-09 21:29:08 +08:00
我倒是希望有人能测下那个远程代理的功能,因为只是我自己拿两台手机测了下,没公测过——而这却是我认为这个 app 里最重要的功能。
novato
2019-10-09 22:51:06 +08:00
因为 socks5 可不只是能代理浏览器,很多应用都可通过它代理。
比如你同事在公司,你在家里,你要 ssh 到公司服务器,就可用他的手机代理过去:
ssh -o ProxyCommand='nc -x 你手机内网 ip:57200 %h %p' 用户名 @公司服务器内网 ip
我自己一个人没法测很多应用场景。但目前不支持 udp 代理,只简单做了 tcp 的代理。如果有需要的话以后再加上

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/606960

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX