V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
novato
V2EX  ›  分享创造

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

  •  
  •   novato · 2019-10-08 11:58:09 +08:00 · 1352 次点击
    这是一个创建于 1633 天前的主题,其中的信息可能已经有所发展或是发生改变。

    开始的想法很简单,就是能通过 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

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

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